C++類模板和模板類
阿新 • • 發佈:2018-11-29
C++
中有一個重要特性,那就是模板型別。類似於Objective-C中的泛型。C++通過類模板來實現泛型支援。
1 基礎的類模板
類模板,可以定義相同的操作,擁有不同資料型別的成員屬性。
通常使用template來宣告。告訴編譯器,碰到T不要報錯,表示一種泛型.
如下,宣告一個普通的類模板:
template <typename T> class Complex{ public: //建構函式 Complex(T a, T b) { this->a = a; this->b = b; } //運算子過載 Complex<T> operator+(Complex &c) { Complex<T> tmp(this->a+c.a, this->b+c.b); return tmp; } private: T a; T b; } int main() { //物件的定義,必須宣告模板型別,因為要分配內容 Complex<int> a(10,20); Complex<int> b(20,30); Complex<int> c = a + b; return 0; }
2 模板類的繼承
在模板類的繼承中,需要注意以下幾點:
- 如果父類自定義了建構函式,記得子類要使用建構函式列表來初始化
- 繼承的時候,如果子類不是模板類,則必須指明當前的父類的型別,因為要分配記憶體空間
- 繼承的時候,如果子類是模板類,要麼指定父類的型別,要麼用子類的泛型來指定父類
template <typename T> class Parent{ public: Parent(T p) { this->p = p; } private: T p; }; //如果子類不是模板類,需要指明父類的具體型別 class ChildOne:public Parent<int>{ public: ChildOne(int a,int b):Parent(b) { this->cone = a; } private: int cone; }; //如果子類是模板類,可以用子類的泛型來表示父類 template <typename T> class ChildTwo:public Parent<T>{ public: ChildTwo(T a, T b):Parent<T>(b) { this->ctwo = a; } private: T ctwo; };
3 內部宣告定義普通模板函式和友元模板函式
普通模板函式和友元模板函式,宣告和定義都寫在類的內部,也不會有什麼報錯。
template <typename T> class Complex { //友元函式實現運算子過載 friend ostream& operator<<(ostream &out, Complex &c) { out<<c.a << " + " << c.b << "i"; return out; } public: Complex(T a, T b) { this->a = a; this->b = b; } //運算子過載+ Complex operator+(Complex &c) { Complex temp(this->a + c.a, this->b + c.b); return temp; } //普通加法函式 Complex myAdd(Complex &c1, Complex &c2) { Complex temp(c1.a + c2.a, c1.b + c2.b); return temp; } private: T a; T b; }; int main() { Complex<int> c1(1,2); Complex<int> c2(3,4); Complex<int> c = c1 + c2; cout<<c<<endl; return 0; }
4 內部宣告友元模板函式+外部定義友元模板函式
如果普通的模板函式宣告在內的內部,定義在類的外部,不管是否處於同一個檔案,就跟普通的函式一樣,不會出現任何錯誤提示。但是如果是友元函式就會出現報錯,是因為有二次編譯這個機制存在。
4.1 模板類和模板函式的機制
在編譯器進行編譯的時候,編譯器會產生類的模板函式的宣告,當時實際確認型別後呼叫的時候,會根據呼叫的型別進行再次幫我們生成對應型別的函式宣告和定義。我們稱之為二次編譯。同樣,因為這個機制,會經常報錯找不到類的函式的實現。在模板類的友元函式外部定義時,也會出現這個錯誤。解決方法是 “ 類的前置宣告和函式的前置宣告 ”。
- 按照普通模板函式的樣式處理友元函式
#include <iostream>
using namespace std;
template <typename T>
class Complex {
//友元函式實現運算子過載
friend ostream& operator<<(ostream &out, Complex<T> &c);
public:
Complex(T a, T b);
//運算子過載+
Complex<T> operator+(Complex<T> &c);
//普通加法函式
Complex<T> myAdd(Complex<T> &c1, Complex<T> &c2);
private:
T a;
T b;
};
//友元函式的實現
template <typename T>
ostream& operator<<(ostream &out, Complex<T> &c)
{
out<<c.a << " + " << c.b << "i";
return out;
}
//函式的實現
template <typename T>
Complex<T>::Complex(T a, T b)
{
this->a = a;
this->b = b;
}
template <typename T>
Complex<T> Complex<T>::operator+(Complex<T> &c)
{
Complex temp(this->a + c.a, this->b + c.b);
return temp;
}
template <typename T>
Complex<T> Complex<T>::myAdd(Complex<T> &c1, Complex<T> &c2)
{
Complex temp(c1.a + c2.a, c1.b + c2.b);
return temp;
}
int main()
{
Complex<int> c1(1,2);
Complex<int> c2(3,4);
Complex<int> c = c1 + c2;
cout<<c<<endl;
return 0;
}
- 友元函式的定義寫在類的外部--錯誤資訊
Undefined symbols for architecture x86_64:
"operator<<(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, Complex<int>&)", referenced from:
_main in demo1.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
上面的錯誤資訊,就是典型的二次編譯的錯誤資訊,找不到友元函式的函式實現。所以,如果友元模板函式的定義寫在函式的外部,需要進行類和函式的前置宣告,來讓編譯器找到函式的實現
4.2 前置宣告解決二次編譯問題
- 類的前置宣告
- 友元模板函式的前置宣告
- 友元模板函式宣告需要增加泛型支援
5 宣告和定義分別在不同的檔案(模板函式、模板友元)
類的宣告和實現,分別在不同的檔案下,需要增加一個hpp檔案支援。或者儘量將模板函式與模板友元放在一個檔案下。
- 類的宣告與函式的宣告寫在.h檔案
- 類的實現及函式的實現寫在.cpp檔案
- 將.cpp檔案改成.hpp檔案
- 在主函式中呼叫.hpp檔案,而不是引用.h檔案
如果碰到.h和.hpp檔案都存在的情況下,引用.hpp檔案。
demo2.h檔案
存放類的宣告和函式的宣告
#include <iostream>
using namespace std;
//類的前置宣告
template <typename T>
class Complex;
//友元函式的宣告
template <typename T>
ostream& operator<<(ostream &out, Complex<T> &c);
template <typename T>
class Complex {
//友元函式實現運算子過載
friend ostream& operator<< <T> (ostream &out, Complex<T> &c);
public:
Complex(T a, T b);
//運算子過載+
Complex<T> operator+(Complex<T> &c);
//普通加法函式
Complex<T> myAdd(Complex<T> &c1, Complex<T> &c2);
private:
T a;
T b;
};
demo2.hpp檔案
包括模板函式的實現
#include "demo2.h"
//友元函式的實現
template <typename T>
ostream& operator<<(ostream &out, Complex<T> &c)
{
out<<c.a << " + " << c.b << "i";
return out;
}
//函式的實現
template <typename T>
Complex<T>::Complex(T a, T b)
{
this->a = a;
this->b = b;
}
template <typename T>
Complex<T> Complex<T>::operator+(Complex<T> &c)
{
Complex temp(this->a + c.a, this->b + c.b);
return temp;
}
template <typename T>
Complex<T> Complex<T>::myAdd(Complex<T> &c1, Complex<T> &c2)
{
Complex temp(c1.a + c2.a, c1.b + c2.b);
return temp;
}
main.cpp檔案
需要呼叫hpp檔案
#include <iostream>
using namespace std;
#include "demo2.hpp"
int main()
{
Complex<int> c1(1,2);
Complex<int> c2(3,4);
Complex<int> c = c1 + c2;
cout<<c<<endl;
return 0;
}