C++中有一个重要特性,那就是模板类型。类似于Objective-C中的泛型。C++通过类模板来实现泛型支持。
类模板,可以定义相同的操作,拥有不同数据类型的成员属性。
通常使用template
来声明。告诉编译器,碰到T
不要报错,表示一种泛型.
如下,声明一个普通的类模板:
template
class Complex{
public:
//构造函数
Complex(T a, T b)
{
this->a = a;
this->b = b;
}
//运算符重载
Complex operator+(Complex &c)
{
Complex tmp(this->a+c.a, this->b+c.b);
return tmp;
}
private:
T a;
T b;
}
int main()
{
//对象的定义,必须声明模板类型,因为要分配内容
Complex a(10,20);
Complex b(20,30);
Complex c = a + b;
return 0;
}
在模板类的继承中,需要注意以下两点:
template
class Parent{
public:
Parent(T p)
{
this->p = p;
}
private:
T p;
};
//如果子类不是模板类,需要指明父类的具体类型
class ChildOne:public Parent{
public:
ChildOne(int a,int b):Parent(b)
{
this->cone = a;
}
private:
int cone;
};
//如果子类是模板类,可以用子类的泛型来表示父类
template
class ChildTwo:public Parent{
public:
ChildTwo(T a, T b):Parent(b)
{
this->ctwo = a;
}
private:
T ctwo;
};
普通模板函数和友元模板函数,声明和定义都写在类的内部,也不会有什么报错。正常。
template
class Complex {
//友元函数实现运算符重载
friend ostream& operator<<(ostream &out, Complex &c)
{
out<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 c1(1,2);
Complex c2(3,4);
Complex c = c1 + c2;
cout<
如果普通的模板函数声明在内的内部,定义在类的外部,不管是否处于同一个文件,就跟普通的函数一样,不会出现任何错误提示。但是如果是友元函数就会出现报错,是因为有二次编译
这个机制存在。
4.1 模板类和模板函数的机制
在编译器进行编译的时候,编译器会产生类的模板函数的声明,当时实际确认类型后调用的时候,会根据调用的类型进行再次帮我们生成对应类型的函数声明和定义。我们称之为二次编译
。同样,因为这个机制,会经常报错找不到类的函数的实现
。在模板类的友元函数外部定义时,也会出现这个错误。解决方法是 “ 类的前置声明和函数的前置声明 ”。
按照普通模板函数的样式处理友元函数
#include
using namespace std;
template
class Complex {
//友元函数实现运算符重载
friend ostream& operator<<(ostream &out, Complex &c);
public:
Complex(T a, T b);
//运算符重载+
Complex operator+(Complex &c);
//普通加法函数
Complex myAdd(Complex &c1, Complex &c2);
private:
T a;
T b;
};
//友元函数的实现
template
ostream& operator<<(ostream &out, Complex &c)
{
out<
Complex::Complex(T a, T b)
{
this->a = a;
this->b = b;
}
template
Complex Complex::operator+(Complex &c)
{
Complex temp(this->a + c.a, this->b + c.b);
return temp;
}
template
Complex Complex::myAdd(Complex &c1, Complex &c2)
{
Complex temp(c1.a + c2.a, c1.b + c2.b);
return temp;
}
int main()
{
Complex c1(1,2);
Complex c2(3,4);
Complex c = c1 + c2;
cout<
友元函数的定义写在类的外部--错误信息
Undefined symbols for architecture x86_64:
"operator<<(std::__1::basic_ostream >&, Complex&)", 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
类的声明和实现,分别在不同的文件下,需要增加一个hpp文件支持。或者尽量将模板函数与模板友元放在一个文件下。
如果碰到.h和.hpp文件都存在的情况下,引用.hpp文件。
demo2.h文件
存放类的声明和函数的声明
#include
using namespace std;
//类的前置声明
template
class Complex;
//友元函数的声明
template
ostream& operator<<(ostream &out, Complex &c);
template
class Complex {
//友元函数实现运算符重载
friend ostream& operator<< (ostream &out, Complex &c);
public:
Complex(T a, T b);
//运算符重载+
Complex operator+(Complex &c);
//普通加法函数
Complex myAdd(Complex &c1, Complex &c2);
private:
T a;
T b;
};
demo2.hpp文件
包括模板函数的实现
#include "demo2.h"
//友元函数的实现
template
ostream& operator<<(ostream &out, Complex &c)
{
out<
Complex::Complex(T a, T b)
{
this->a = a;
this->b = b;
}
template
Complex Complex::operator+(Complex &c)
{
Complex temp(this->a + c.a, this->b + c.b);
return temp;
}
template
Complex Complex::myAdd(Complex &c1, Complex &c2)
{
Complex temp(c1.a + c2.a, c1.b + c2.b);
return temp;
}
main.cpp文件
需要调用hpp文件
#include
using namespace std;
#include "demo2.hpp"
int main()
{
Complex c1(1,2);
Complex c2(3,4);
Complex c = c1 + c2;
cout<
作者:一月二十三
链接:https://www.jianshu.com/p/70ca94872418
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。