一、類模板
1、作用
建立一個通用類,類中的成員 數據類型可以不具體制定,用一個虛擬的類型來代表。
2、語法
template<typename T> 類
template --- 聲明創建模板
typename --- 表面其后面的符號是一種數據類型,可以用class代替
T --- 通用的數據類型,名稱可以替換,通常為大寫字母
二、類模板和函數模板的區別
1、 類模板沒有自動類型推導的使用方式
2、 類模板在模板參數列表中可以有默認參數
#include <bits/stdc++.h>
using namespace std; const int maxn = 1e5 + 5; //類模板
template <class NameType=string, class AgeType = int>
class Person { public: Person(NameType name, AgeType age) { this->mName = name; this->mAge = age; } void showPerson() { cout << "name: " << this->mName << " age: " << this->mAge << endl; } public: NameType mName; AgeType mAge; }; //1、類模板沒有自動類型推導的使用方式
void test01() { // Person p("孫悟空", 1000); // 錯誤 類模板使用時候,不可以用自動類型推導
Person<string, int> p("孫悟空", 1000); //必須使用顯示指定類型的方式,使用類模板
p.showPerson(); } //2、類模板在模板參數列表中可以有默認參數
void test02() { Person<> p("豬八戒", 999); //類模板中的模板參數列表 可以指定默認參數
p.showPerson(); } int main() { test01(); test02(); system("pause"); return 0; }
三、類模板中成員函數創建時機
類模板中成員函數和普通類中成員函數創建時機是有區別的:
1、普通類中的成員函數一開始就可以創建
2、類模板中的成員函數在調用時才創建
#include <bits/stdc++.h>
using namespace std; const int maxn = 1e5 + 5; class Person1 { public: void showPerson1() { cout << "Person1 show" << endl; } }; class Person2 { public: void showPerson2() { cout << "Person2 show" << endl; } }; template <class T>
class MyClass { public: T obj; //類模板中的成員函數,並不是一開始就創建的,而是在模板調用時再生成
void fun1() { obj.showPerson1(); } void fun2() { obj.showPerson2(); } }; void test01() { MyClass<Person1> m; m.fun1(); //m.fun2();//編譯會出錯,說明函數調用才會去創建成員函數
} int main() { test01(); system("pause"); return 0; }
四、類模板對象做函數參數
一共有三種傳入方式:
1. 指定傳入的類型 --- 直接顯示對象的數據類型
2. 參數模板化 --- 將對象中的參數變為模板進行傳遞
3. 整個類模板化 --- 將這個對象類型 模板化進行傳遞
一般使用第一種方式,因為比較簡易
template <class NameType, class AgeType = int>
class Person { public: Person(NameType name, AgeType age) { this->mName = name; this->mAge = age; } void showPerson() { cout << "name: " << this->mName << " age: " << this->mAge << endl; } public: NameType mName; AgeType mAge; }; //1、指定傳入的類型
void printPerson1(Person<string, int> &p) { p.showPerson(); }
其他兩種:

1 #include <bits/stdc++.h> 2 using namespace std; 3 const int maxn = 1e5 + 5; 4 //類模板 5 template <class NameType, class AgeType = int> 6 class Person 7 { 8 public: 9 Person(NameType name, AgeType age) 10 { 11 this->mName = name; 12 this->mAge = age; 13 } 14 void showPerson() 15 { 16 cout << "name: " << this->mName << " age: " << this->mAge << endl; 17 } 18 19 public: 20 NameType mName; 21 AgeType mAge; 22 }; 23 //1、指定傳入的類型 24 void printPerson1(Person<string, int> &p) 25 { 26 p.showPerson(); 27 } 28 void test01() 29 { 30 Person<string, int> p("孫悟空", 100); 31 printPerson1(p); 32 } 33 //2、參數模板化 34 template <class T1, class T2> 35 void printPerson2(Person<T1, T2> &p) 36 { 37 p.showPerson(); 38 cout << "T1的類型為: " << typeid(T1).name() << endl; 39 cout << "T2的類型為: " << typeid(T2).name() << endl; 40 } 41 void test02() 42 { 43 Person<string, int> p("豬八戒", 90); 44 printPerson2(p); 45 } 46 //3、整個類模板化 47 template <class T> 48 void printPerson3(T &p) 49 { 50 cout << "T的類型為: " << typeid(T).name() << endl; 51 p.showPerson(); 52 } 53 void test03() 54 { 55 Person<string, int> p("唐僧", 30); 56 printPerson3(p); 57 } 58 int main() 59 { 60 test01(); 61 test02(); 62 test03(); 63 system("pause"); 64 return 0; 65 }
五、類模板與繼承
當類模板碰到繼承時,需要注意一下幾點:
1、當子類繼承的父類是一個類模板時,子類在聲明的時候,要指定出父類中T的類型
2、如果不指定,編譯器無法給子類分配內存
3、如果想靈活指定出父類中T的類型,子類也需變為類模板
#include <bits/stdc++.h>
using namespace std; const int maxn = 1e5 + 5; template <class T>
class Base { T m; }; //class Son:public Base //錯誤,c++編譯需要給子類分配內存,必須知道父類中T的類型才可以向下繼承
class Son : public Base<int> //必須指定一個類型
{ }; void test01() { Son c; } //類模板繼承類模板 ,可以用T2指定父類中的T類型
template <class T1, class T2>
class Son2 : public Base<T2> { public: Son2() { cout << typeid(T1).name() << endl; cout << typeid(T2).name() << endl; } }; void test02() { Son2<int, char> child1; } int main() { test01(); test02(); system("pause"); return 0; }
六、類模板成員函數類外實現
#include <bits/stdc++.h>
using namespace std; const int maxn = 1e5 + 5; //類模板中成員函數類外實現
template <class T1, class T2>
class Person { public: //成員函數類內聲明
Person(T1 name, T2 age); void showPerson(); public: T1 m_Name; T2 m_Age; }; //構造函數 類外實現
template <class T1, class T2> Person<T1, T2>::Person(T1 name, T2 age) { this->m_Name = name; this->m_Age = age; } //成員函數 類外實現
template <class T1, class T2>
void Person<T1, T2>::showPerson() { cout << "姓名: " << this->m_Name << " 年齡:" << this->m_Age << endl; } void test01() { Person<string, int> p("Tom", 20); p.showPerson(); } int main() { test01(); system("pause"); return 0; }
七、類模板分文件編寫
問題:類模板中成員函數創建時機是在調用階段,導致分文件編寫時鏈接不到
解決:
1、直接包含.cpp源文件
2、將聲明和實現寫到同一個文件中,並更改后綴名為.hpp,hpp是約定的名稱,並不是強制
person.hpp
#pragma once #include <iostream>
using namespace std; #include <string> template <class T1, class T2>
class Person { public: Person(T1 name, T2 age); void showPerson(); public: T1 m_Name; T2 m_Age; }; //構造函數 類外實現
template <class T1, class T2> Person<T1, T2>::Person(T1 name, T2 age) { this->m_Name = name; this->m_Age = age; } //成員函數 類外實現
template <class T1, class T2>
void Person<T1, T2>::showPerson() { cout << "姓名: " << this->m_Name << " 年齡:" << this->m_Age << endl; }
類模板分文件編寫.cpp中代碼
#include <bits/stdc++.h>
using namespace std; #include "person.cpp" //解決方式1,包含cpp源文件 //解決方式2,將聲明和實現寫到一起,文件后綴名改為.hpp
#include "person.hpp"
void test01() { Person<string, int> p("Tom", 10); p.showPerson(); } int main() { test01(); system("pause"); return 0; }
八、類模板與友元
1、全局函數類內實現 - 直接在類內聲明友元即可(這個實現起來簡單)
2、全局函數類外實現 - 需要提前讓編譯器知道全局函數的存在
#include <bits/stdc++.h>
using namespace std; //2、全局函數配合友元 類外實現 - 先做函數模板聲明,下方在做函數模板定義,在做友元
template <class T1, class T2>
class Person; //如果聲明了函數模板,可以將實現寫到后面,否則需要將實現體寫到類的前面讓編譯器提前看到 //template<class T1, class T2> void printPerson2(Person<T1, T2> & p);
template <class T1, class T2>
void printPerson2(Person<T1, T2> &p) { cout << "類外實現 ---- 姓名: " << p.m_Name << " 年齡:" << p.m_Age << endl; } template <class T1, class T2>
class Person { //1、全局函數配合友元 類內實現
friend void printPerson(Person<T1, T2> &p) { cout << "姓名: " << p.m_Name << " 年齡:" << p.m_Age << endl; } //全局函數配合友元 類外實現
friend void printPerson2<>(Person<T1, T2> &p); public: Person(T1 name, T2 age) { this->m_Name = name; this->m_Age = age; } private: T1 m_Name; T2 m_Age; }; //1、全局函數在類內實現
void test01() { Person<string, int> p("Tom", 20); printPerson(p); } //2、全局函數在類外實現
void test02() { Person<string, int> p("Jerry", 30); printPerson2(p); } int main() { //test01();
test02(); system("pause"); return 0; }