C++中有一個(gè)重要特性,那就是模板類型眨业。類似于Objective-C中的泛型急膀。C++通過(guò)類模板來(lái)實(shí)現(xiàn)泛型支持。
1 基礎(chǔ)的類模板
類模板龄捡,可以定義相同的操作卓嫂,擁有不同數(shù)據(jù)類型的成員屬性。
通常使用template
來(lái)聲明聘殖。告訴編譯器晨雳,碰到T
不要報(bào)錯(cuò),表示一種泛型.
如下奸腺,聲明一個(gè)普通的類模板:
template <typename T>
class Complex{
public:
//構(gòu)造函數(shù)
Complex(T a, T b)
{
this->a = a;
this->b = b;
}
//運(yùn)算符重載
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()
{
//對(duì)象的定義餐禁,必須聲明模板類型,因?yàn)橐峙鋬?nèi)容
Complex<int> a(10,20);
Complex<int> b(20,30);
Complex<int> c = a + b;
return 0;
}
2 模板類的繼承
在模板類的繼承中突照,需要注意以下兩點(diǎn):
- 如果父類自定義了構(gòu)造函數(shù)帮非,記得子類要使用構(gòu)造函數(shù)列表來(lái)初始化
- 繼承的時(shí)候,如果子類不是模板類,則必須指明當(dāng)前的父類的類型喜鼓,因?yàn)橐峙鋬?nèi)存空間
- 繼承的時(shí)候副砍,如果子類是模板類,要么指定父類的類型庄岖,要么用子類的泛型來(lái)指定父類
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;
};
//如果子類是模板類,可以用子類的泛型來(lái)表示父類
template <typename T>
class ChildTwo:public Parent<T>{
public:
ChildTwo(T a, T b):Parent<T>(b)
{
this->ctwo = a;
}
private:
T ctwo;
};
3 內(nèi)部聲明定義普通模板函數(shù)和友元模板函數(shù)
普通模板函數(shù)和友元模板函數(shù)隅忿,聲明和定義都寫在類的內(nèi)部心剥,也不會(huì)有什么報(bào)錯(cuò)。正常背桐。
template <typename T>
class Complex {
//友元函數(shù)實(shí)現(xiàn)運(yùn)算符重載
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;
}
//運(yùn)算符重載+
Complex operator+(Complex &c)
{
Complex temp(this->a + c.a, this->b + c.b);
return temp;
}
//普通加法函數(shù)
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 內(nèi)部聲明友元模板函數(shù)+外部定義友元模板函數(shù)
如果普通的模板函數(shù)聲明在內(nèi)的內(nèi)部优烧,定義在類的外部,不管是否處于同一個(gè)文件链峭,就跟普通的函數(shù)一樣畦娄,不會(huì)出現(xiàn)任何錯(cuò)誤提示。但是如果是友元函數(shù)就會(huì)出現(xiàn)報(bào)錯(cuò)弊仪,是因?yàn)橛?code>二次編譯這個(gè)機(jī)制存在熙卡。
4.1 模板類和模板函數(shù)的機(jī)制
在編譯器進(jìn)行編譯的時(shí)候,編譯器會(huì)產(chǎn)生類的模板函數(shù)的聲明励饵,當(dāng)時(shí)實(shí)際確認(rèn)類型后調(diào)用的時(shí)候驳癌,會(huì)根據(jù)調(diào)用的類型進(jìn)行再次幫我們生成對(duì)應(yīng)類型的函數(shù)聲明和定義。我們稱之為二次編譯
役听。同樣颓鲜,因?yàn)檫@個(gè)機(jī)制,會(huì)經(jīng)常報(bào)錯(cuò)找不到類的函數(shù)的實(shí)現(xiàn)
典予。在模板類的友元函數(shù)外部定義時(shí)甜滨,也會(huì)出現(xiàn)這個(gè)錯(cuò)誤。解決方法是 “ 類的前置聲明和函數(shù)的前置聲明 ”熙参。
按照普通模板函數(shù)的樣式處理友元函數(shù)
#include <iostream>
using namespace std;
template <typename T>
class Complex {
//友元函數(shù)實(shí)現(xiàn)運(yùn)算符重載
friend ostream& operator<<(ostream &out, Complex<T> &c);
public:
Complex(T a, T b);
//運(yùn)算符重載+
Complex<T> operator+(Complex<T> &c);
//普通加法函數(shù)
Complex<T> myAdd(Complex<T> &c1, Complex<T> &c2);
private:
T a;
T b;
};
//友元函數(shù)的實(shí)現(xiàn)
template <typename T>
ostream& operator<<(ostream &out, Complex<T> &c)
{
out<<c.a << " + " << c.b << "i";
return out;
}
//函數(shù)的實(shí)現(xiàn)
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;
}
友元函數(shù)的定義寫在類的外部--錯(cuò)誤信息
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)
上面的錯(cuò)誤信息艳吠,就是典型的二次編譯的錯(cuò)誤信息,找不到友元函數(shù)的函數(shù)實(shí)現(xiàn)孽椰。所以昭娩,如果友元模板函數(shù)的定義寫在函數(shù)的外部,需要進(jìn)行類和函數(shù)的前置聲明黍匾,來(lái)讓編譯器找到函數(shù)的實(shí)現(xiàn)
4.2 前置聲明解決二次編譯問(wèn)題
- 類的前置聲明
- 友元模板函數(shù)的前置聲明
- 友元模板函數(shù)聲明需要增加泛型支持
5 聲明和定義分別在不同的文件(模板函數(shù)栏渺、模板友元)
類的聲明和實(shí)現(xiàn),分別在不同的文件下锐涯,需要增加一個(gè)hpp文件支持磕诊。或者盡量將模板函數(shù)與模板友元放在一個(gè)文件下。
- 類的聲明與函數(shù)的聲明寫在.h文件
- 類的實(shí)現(xiàn)及函數(shù)的實(shí)現(xiàn)寫在.cpp文件
- 將.cpp文件改成.hpp文件
- 在主函數(shù)中調(diào)用.hpp文件霎终,而不是引用.h文件
如果碰到.h和.hpp文件都存在的情況下滞磺,引用.hpp文件。
demo2.h文件
存放類的聲明和函數(shù)的聲明
#include <iostream>
using namespace std;
//類的前置聲明
template <typename T>
class Complex;
//友元函數(shù)的聲明
template <typename T>
ostream& operator<<(ostream &out, Complex<T> &c);
template <typename T>
class Complex {
//友元函數(shù)實(shí)現(xiàn)運(yùn)算符重載
friend ostream& operator<< <T> (ostream &out, Complex<T> &c);
public:
Complex(T a, T b);
//運(yùn)算符重載+
Complex<T> operator+(Complex<T> &c);
//普通加法函數(shù)
Complex<T> myAdd(Complex<T> &c1, Complex<T> &c2);
private:
T a;
T b;
};
demo2.hpp文件
包括模板函數(shù)的實(shí)現(xiàn)
#include "demo2.h"
//友元函數(shù)的實(shí)現(xiàn)
template <typename T>
ostream& operator<<(ostream &out, Complex<T> &c)
{
out<<c.a << " + " << c.b << "i";
return out;
}
//函數(shù)的實(shí)現(xiàn)
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文件
需要調(diào)用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;
}