C++類模板,你看我就夠了

轉自:
https://www.jianshu.com/p/70ca94872418

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 前置聲明解決二次編譯問題

類的前置聲明
友元模板函數的前置聲明
友元模板函數聲明需要增加泛型支持

前置聲明.png

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;

}

作者:一月二十三
鏈接:https://www.jianshu.com/p/70ca94872418
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯繫作者獲得授權並註明出處。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章