一、定义一个模板类声明
template<typename T1, typename T2>
class Complex;
template<typename T1, typename T2>
Complex<T1, T2> MySub(Complex<T1, T2> a, Complex<T1, T2> b);
template<typename T1,typename T2>
class Complex
{
friend Complex MySub<T1,T2>(Complex a, Complex b);
friend ostream& operator<< <T1, T2>(ostream &out, const Complex&c1);
public:
Complex(T1 a, T2 b);
Complex& operator+( Complex& c1);
private:
T2 b;
T1 a;
};
在上面的生命中有四种函数,分别是:
1.模板类Complex的成员函数;2.普通操作符重载;3.友元操作符重载函数;4.友元非操作符重载函数。
对于上面这4种类型的函数,模板类的函数实现分别是这样实现的:
1.模板类Complex的成员函数;
Complex(T1 a, T2 b);
函数在类的外部实现代码
template <typename T1, typename T2>
Complex<T1, T2>::Complex(T1 a, T2 b)
{
this->a = a;
this->b = b;
}
2.普通操作符重载:
Complex& operator+( Complex& c1);
函数在类的外部实现代码:
template <typename T1,typename T2>
Complex<T1,T2>& Complex<T1, T2>::operator+( Complex& c1)
{
this->a = this->a + c1.a;
this->b = this->b + c1.b;
return *this;
}
3.友元操作符重载函数
friend ostream& operator<< <T1, T2>(ostream &out, const Complex&c1);
函数在类的外部实现代码:
template<typename T1,typename T2>
ostream& operator<<(ostream &out, const Complex<T1,T2>&c1)
{
out << "a=" << c1.a << ":b=" << c1.b << endl;
return out;
}
注意:通过观察模板类的友元函数的定义,我们可以观察到,在函数名的后面添加了<T1,T2>,为什么要添加<T1,T2>呢?我的理解是:根据友元函数本身的性质,友元函数的本质是全局函数,只不过是在类的内部声明罢了,赋予友元函数可以访问类内部的私有成员和保护成员的功能,那么在函数声明时,在函数名的后面添加上<T1,T2>,便于实例化模板类时要将具体的类型传给友元函数。
4.友元非操作符重载函数
friend Complex MySub<T1,T2>(Complex a, Complex b);
程序在类外部的实现代码
<pre name="code" class="cpp">template<typename T1, typename T2> //类的前置定义
class Complex;
template<typename T1, typename T2> //函数的前置定义
Complex<T1, T2> MySub(Complex<T1, T2> a, Complex<T1, T2> b);
template<typename T1,typename T2>
Complex<T1, T2> MySub(Complex<T1, T2> a, Complex<T1, T2> b)
{
Complex<T1,T2> tmp(a.a - b.a, a.b - b.b);
return tmp;
}
注意:在类的外部实现友元非操作符重载函数时,需要在函数前面重新:写类的前置定义和函数的前置定义,当然了由于是友元函数,所以
在模板类内部声明函数时,也需要在函数名后面,添加上类型<T1,T2>,便于实例化模板类时,将类型传递给友元函数。
【编译器关于模板类的实现机制】
模板方法和模板类和普通的函数相比有两步初始化的过程:1.类型初始化,2.参数初始化。
在分离式编译的环境下,编译器编译某一个.cpp文件时并不知道另一个.cpp文件的存在,也不会去查找(当遇到未决符号时它会寄希望于连接器)。这种模式在没有模板的情况下运行良好,但遇到模板时就傻眼了,因为模板仅在需要的时候才会实例化出来,所以,当编译器只看到模板的声明时,它不能实例化该模板,只能创建一个具有外部连接的符号并期待连接器能够将符号的地址决议出来。然而当实现该模板的.cpp文件中没有用到模板的实例时,编译器懒得去实例化,所以,整个工程的.obj中就找不到一行模板实例的二进制代码,于是连接器也黔驴技穷了。
所以在写模板类是最好将模板函数的实现和声明写到同一个文件中(*.hpp)