C++中的类模板
文章目錄
- 1 C++中的類模板簡介
- 2 繼承中類模板的使用
- 2.1 父類一般類、子類是模板類
- 2.2 子類一般類、父類是模板類
- 2.3 父類和子類均為模板類
- 3 類模板的工程應用
- 4 多參數類模板和類模板的特化
- 4.1 多參數類模板
- 4.2 類模板的特化
- 4.3 重定義和特化的不同
- 5 模板類的特殊用法
- 5.1 模板類遇到友元函數
- 5.2 類模板遇上靜態成員
1 C++中的類模板簡介
我們知道,有些類主要用于存儲和組織數據元素,類中數據組織的方式和數據元素的具體類型無關,如:數組類、鏈表類、Stack類、Queue類等。C++中將模板的思想應用于類,使得類的實現不關注數據元素的具體類型,而只關注類所需要實現的功能。
C++中的類模板:
- 以相同的方式處理不同的類型。
- 在類聲明前使用template進行標識。
- <typename T>用于說明類中使用的泛指類型T。
- 聲明的泛指類型T可以出現在類模板的任意地方。
類模板的應用:
- 只能顯示指定具體類型,無法自動推導。
- 使用具體類型<Type>定義對象。
編譯器對類模板的處理方式和函數模板相同:
- 從類模板通過具體類型產生不同的類。
- 在聲明的地方對類模板代碼本身進行編譯。
- 在使用的地方對參數替換后的代碼進行編譯(成員函數也是分步編譯,調用了哪個成員函數就對哪個成員函數進行編譯)。
編程實驗:類模板初探
#include <iostream> #include <string>using namespace std;template < typename T > class Operator { public:T add(T a, T b){return a + b;}T minus(T a, T b){return a - b;}T multiply(T a, T b){return a * b;}T divide(T a, T b){return a / b;} };string operator-(string& l, string& r) {return "Minus"; }int main() {Operator<int> op1;cout << op1.add(1, 2) << endl;Operator<string> op2;cout << op2.add("D.T.", "Software") << endl;cout << op2.minus("D.T", "Software") << endl;return 0; }2 繼承中類模板的使用
2.1 父類一般類、子類是模板類
class B { public:B(int b){this->b = b;}private:int b; };template <typename T> class A : public B { public://函數的參數列表使用虛擬類型A(T t) : B (0){this->t = t;}//成員函數返回值使用虛擬類型T &getT(){return t;}private://成員變量使用虛擬類型T t; };父類一般類,子類是模板類, 和普通繼承的玩法類似。
2.2 子類一般類、父類是模板類
template <typename T> class A { public://函數的參數列表使用虛擬類型A(T t){this->t = t;}//成員函數返回值使用虛擬類型T &getT(){return t;}private://成員變量使用虛擬類型T t; };class B : public A<int> { public:B(int b){this->b = b;}private:int b; };子類是一般類,父類是模板類,繼承時必須在子類里實例化父類的類型參數。
2.3 父類和子類均為模板類
template <typename T> class A { public://函數的參數列表使用虛擬類型A(T t){this->t = t;}//成員函數返回值使用虛擬類型T &getT(){return t;}private://成員變量使用虛擬類型T t; };template <typename Tb> class B: public A<Tb> {public:B(Tb b):A<Tb>(b){this->b = b;}private:Tb b;};父類和子類都時模板類時,子類的虛擬的類型可以傳遞到父類中。
3 類模板的工程應用
類模板的工程應用:
- 類模板必須在頭文件中定義。
- 類模板不能分開實現在不同的文件中。
- 類模板外部定義的成員函數需要加上模板<>聲明,成員函數的內部加不加都是可以的。
編程實驗:模板類的工程應用
operator.h:
main.cpp:
#include <iostream> #include <string> #include "Operator.h"using namespace std;int main() {Operator<int> op1;cout << op1.add(1, 2) << endl;cout << op1.multiply(4, 5) << endl;cout << op1.minus(5, 6) << endl;cout << op1.divide(10, 5) << endl;return 0; }我們在有些地方也會看到類模板的聲明和實現是放在兩個不同的文件中,但是因為類模板的特殊實現,我們應在使用類模板時使用#include 包含 實現部分的.cpp 或.hpp文件(如果一個文件是hpp結尾,那么代表當前文件是類模板的實現)。這樣的作法顯然有點過于繁瑣,因此還是定義在同一個文件中比較合適!
4 多參數類模板和類模板的特化
4.1 多參數類模板
類模板可以定義任意多個不同的類型參數。
4.2 類模板的特化
類模板可以被特化:
- 指定類模板的特化實現。
- 部分類型參數必須顯示指定。
- 根據類型參數分開實現類模板。
類模板的特化類型:
- 部分特化:用特定規則約束類型參數。
- 完全特化:完全顯示指定類型參數。
部分特化:
完全特化:
類模板特化注意事項:
- 特化只是模板的分開實現,本質上是同一個類模板。
- 特化模板的使用方式是統一的,必須顯示指定每一個類型參數。
編程實驗:類模板的特化
#include <iostream> #include <string>using namespace std;template < typename T1, typename T2 > class Test { public:void add(T1 a, T2 b){cout << "void add(T1 a, T2 b)" << endl;cout << a + b << endl;} };template < typename T1, typename T2 > class Test < T1*, T2* > // 關于指針的特化實現 { public:void add(T1* a, T2* b){cout << "void add(T1* a, T2* b)" << endl;cout << *a + *b << endl;} };template < typename T > class Test < T, T > // 當 Test 類模板的兩個類型參數完全相同時,使用這個實現 { public:void add(T a, T b){cout << "void add(T a, T b)" << endl;cout << a + b << endl;}void print(){cout << "class Test < T, T >" << endl;} };template < > class Test < void*, void* > // 當 T1 == void* 并且 T2 == void* 時 { public:void add(void* a, void* b){cout << "void add(void* a, void* b)" << endl;cout << "Error to add void* param..." << endl;} };int main() { Test<int, float> t1;Test<long, long> t2;Test<void*, void*> t3;t1.add(1, 2.5);t2.add(5, 5);t2.print();t3.add(NULL, NULL);Test<int*, double*> t4;int a = 1;double b = 0.1;t4.add(&a, &b);return 0; }4.3 重定義和特化的不同
重定義:
- 一個類模板和一個新類(或者兩個類模板)。
- 使用的時候需要考慮如何選擇的問題。
特化:
- 以統一的方式使用類模板和特化類。
- 編譯器自動優先選擇特化類。
5 模板類的特殊用法
5.1 模板類遇到友元函數
#include <iostream>using namespace std;template <typename T> class A { public:A(T t=0);//聲明一個友元函數,實現對兩個A類對象進行加法操作template <typename T>friend A<T> addA(const A<T> &a, const A<T> &b);/* 也可以寫成如下形式:friend A<T> addA <T>(const A<T> &a, const A<T> &b);*/T &getT();A operator +(const A &other);void print();private:T t; };template <typename T> A<T>::A(T t) {this->t = t; }template <typename T> T &A<T>::getT(){return t;}template <typename T> A<T> A<T>::operator+(const A<T> &other){A tmp; //類的內部類型可以顯示聲明也可以不顯示tmp.t =this->t + other.t;return tmp;}template <typename T> void A<T>::print(){cout<<this->t<<endl; }//A 類的友元函數,就是它的好朋友 template <typename T> A<T> addA(const A<T> &a, const A<T> &b){A<T> tmp;cout<<"call addA()..."<<endl;tmp.t = a.t + b.t;return tmp; }int main(void){A<int> a(666), b(888);//cout<<a.getT()<<endl;A<int> tmp = a + b;A<int> tmp1 = addA<int>(a, b);tmp.print();tmp1.print();system("pause");return 0; }結論:
(1) 類內部聲明友元函數,必須寫成一下形式
(2) 友元函數實現 必須寫成
template<typename T>A<T> add(A<T> &a, A<T> &b) {//...... }(3)友元函數調用 必須寫成A<int> c4 = addA<int>(c1, c2);。
5.2 類模板遇上靜態成員
#include <iostream>using namespace std;template <typename T> class A { public:A(T t=0);T &getT();A operator +(const A &other);void print();public:static int count; private:T t; };template <typename T> int A<T>::count = 666;template <typename T> A<T>::A(T t) {this->t = t; }template <typename T> T &A<T>::getT() {return t; }template <typename T> A<T> A<T>::operator+(const A<T> &other){A tmp; //類的內部類型可以顯示聲明也可以不顯示tmp.t =this->t + other.t;return tmp; }template <typename T> void A<T>::print(){cout<<this->t<<endl; }/* //當我們的虛擬的類型T被 int 實例化以后,模板類如下: class A { public: A(int t=0);int &getT();A operator +(const A &other);void print();public: static int count; private: int t; };int A::count = 666;A::A(int t) { this->t = t; }int &A::getT() { return t; }A A::operator+(const A &other){ A tmp; //類的內部類型可以顯示聲明也可以不顯示 tmp.t =this->t + other.t; return tmp; }void A::print(){ cout<<this->t<<endl; } *//* //當我們的虛擬的類型T被 float 實例化以后,模板類如下: class A { public: A(float t=0);float &getT();A operator +(const A &other);void print();public: static int count; private: float t; };int A::count = 666;A::A(float t) { this->t = t; }float &A::getT() { return t; }A A::operator+(const A &other){ A tmp; //類的內部類型可以顯示聲明也可以不顯示 tmp.t =this->t + other.t; return tmp; }void A::print(){ cout<<this->t<<endl; } */int main(void){A<int> a(666), b(888);A<int> tmp = a + b;//A a(666), b(888);//A tmp = a + b;A<float> c(777), d(999);a.count = 888;cout<<"b.count:"<<b.count<<endl;cout<<"c.count:"<<c.count<<endl;cout<<"d.count:"<<d.count<<endl;c.count = 1000;cout<<"修改后, d.count:"<<d.count<<endl;//tmp.print();system("pause");return 0; }總結:
- 從類模板實例化的每個模板類有自己的類模板數據成員,該模板類的所有對象共享一個static數據成員。
- 和非模板類的static數據成員一樣,模板類的static數據成員也應該在文件范圍定義和初始化。
- static 數據成員也可以使用虛擬類型參數T。
參考資料:
總結
- 上一篇: 基金投资如何获得收益 不同基金存在一定
- 下一篇: 芝麻信用分从600提升到700