一、函数模板
函数模板格式(其中class可以替换为typename):
1 2 3 4 5
| template <class 类型参数1, class 类型参数2, ...> 返回值类型 模板名(参数名) { 函数体; }
|
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| #include<iostream> using namespace std;
template <typename T> void cswap(T &x, T &y) { T tmp = x; x = y; y = tmp; }
template <typename T> T test(T o) { return o+1; }
int main() { int n = 1, m = 2; cswap(n, m); cout<<n<<" "<<m<<endl; double a = 1.1, b = 2.2; cswap(a, b); cout<<a<<" "<<b<<endl; cout<<test(1)<<endl; cout<<test(1.2)<<endl; cout<<test<double>(3)<<endl; return 0; }
|
函数模板是可以重载的,形参表不一样即可。在多个函数和函数模板名字相同的情况下,一条函数的调用语句遵循如下顺序:
- 参数完全匹配的普通函数
- 参数完全匹配的模板函数
- 实参经过自动类型转换后能够匹配的普通函数
- 都找不到,编译出错
下面是一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include<iostream> using namespace std;
template <class T> T test(T a, T b) { cout<<"test1"<<endl; }
template <class T, class T1> T test(T a, T1 b) { cout<<"test2"<<endl; }
double test(double a, double b) { cout<<"test3"<<endl; }
int main() { test(1, 2); test(1, 2.0); test(1.0, 2); test(1.0, 2.0); return 0; }
|
输出结果是:
下面来做一个实验:如果把第二个模板去掉,按照我们之前说的规则,程序将默认test(1, 2.0);和test(1.0, 2);调用普通函数也就是输出三次test3。如果把第二个模板和普通函数去掉,编译将出错,出错的将会是test(1, 2.0);和test(1.0, 2);这两条语句,因为编译器不知道该把1换成double类型,还是把2.0换成int类型,出现了二义性,因此编译出错。
二、类模板
类模板格式(其中class可以替换为typename):
1 2 3 4 5 6 7 8 9 10 11 12 13
| template <class 类型参数1, class 类型参数2, ...> class 类模板名 { 成员函数和成员变量 }
template <class 类型参数1, class 类型参数2, ...> 返回值类型 类模板名<class 类型参数1, class 类型参数2, ...>::成员函数名(参数名) { do sth; }
|
由类模板实例化得到的类叫做模板类。
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include<iostream> using namespace std;
template <class T> class A{ public: T n; template<class T2> T2 test(T2 a); };
template <class T> template <class T2> T2 A<T>::test(T2 a) { return a+1; }
int main() { A <int>p; cout<<p.test(1)<<endl; return 0; }
|
注意一下上面一些细节即可。
三、类模板与继承
有以下几种情况:
1.类模板→派生→类模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include<iostream> using namespace std;
template<class T1, class T2> class A{ T1 n; T2 m; };
template<class T1, class T2> class B:public A<T2, T1>{ T1 n1; T2 m1; };
template<class T> class C:public B<T, T>{ T n2; T m2; };
int main() { B <int, double>b; C <int>c; return 0; }
|
十分easy,把所有T、T1、T2换成我们相应写的类型就可以了。生成对象b时,类B实例化成:class B<int, double>,类A实例化成:class A<double, int>。生成对象c时,类C实例化成:class C<int>,类B实例化成:class B<int, int>,类A实例化成:class A<int, int>。
2.模板类→派生→类模板
这个其实就是类A派生类B的时候,类B把类A的类型参数表给填了,也即是类A被实例化,变成模板类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| template<class T1, class T2> class A{ T1 n; T2 m; };
template<class T3, class T4> class B:public A<int, double>{
T n1; T m1; };
int main() { B <int>b; return 0; }
|
3.普通类→派生→类模板
这个就更easy了,直接看下面的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13
| class A{ int n; };
template<class T> class B:public A{ T m; };
int main(){ B <double>b; return 0; }
|
4.模板类→派生→普通类
其实就是把上面的例子反过来而已:
1 2 3 4 5 6 7 8 9 10 11 12 13
| template<class T> class A{ T n; };
class B:public A<double>{ int m; };
int main(){ B b; return 0; }
|
上面这四种情况不用死记硬背,用模板类还是类模板继承看需求。
四、类模板与友元
1.函数、类、类的成员函数作为类模板的友元
这个还用说???
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| void test(){ } class A{ };
class B{ public: void fun(){ } };
template<class T> class C{ friend void test(); friend class A; friend void B::fun(); };
int main(){ C <int>c; return 0; }
|
2.函数模板作为类模板的友元
把上面的改动一下就是了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| template <class T1> void test(T1 t){ }
class A{ };
class B{ public: void fun(){ } };
template<class T> class C{ template <class T1> friend void test(T1 t); friend class A; friend void B::fun(); };
|
3.函数模板作为类的友元
1 2 3 4 5 6 7 8
| template<class T> void fun(T n){ }
class A{ public: template<class T> friend void fun(T n){ } };
|
4.类模板作为类模板的友元
这个稍微复杂一些:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| #include<iostream> using namespace std;
template<class T2> class B{ public: T2 m; template <class T1> friend class A; };
template<class T1> class A{ public: T1 n; template <class T2> void test(B <T2>b) { b.m = 100; cout<<b.m<<endl; } };
int main() { B <int>b; A <int>a; a.test(b); return 0; }
|
五、类模板中的静态成员
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| #include<iostream> using namespace std;
template<class T> class A{ private: T n; public: static T sum; A(T _n) { n=_n; sum=sum+n; } ~A() { sum=sum-n; } static void Print(){ cout<<sum<<endl; }
};
template<> int A<int>::sum = 0;
template<> double A<double>::sum = 0;
int main() { A <int>a1(2); A <int>a2(3); a1.Print(); A <double>a3(2.2); A <double>a4(3.6); a3.Print(); return 0; }
|
输出结果:
从这个输出结果可以看出,A<int>和A<double>是两个不同的类,它们不会共享同一个静态成员变量sum。说白了,还是那句话,静态成员变量是属于一个类的,不属于某一个对象。