第22课 - 类模板 - 下
一.类模板的局部特化
1.1 类模板可以定义多个类型参数
Source Example 1.1: #include <iostream> /* run this program using the console pauser or add your own getch, system("pause") or input loop */ using namespace std; template <typename T1, typename T2> class Test { public: void print(T1 i, T2 j) { cout <<i + j <<endl; } }; int main(int argc, char** argv) { Test<double, int> t1; /* 输出10.8001 */ t1.print(10.001,8); return 0; }
1.2 类模板可以被局部特化
- 可以指定类模板的特定实现,并且要求某些类型参数依然必须得模板的用户指定。
Source Example 1.2: #include <iostream> /* run this program using the console pauser or add your own getch, system("pause") or input loop */ using namespace std; template <typename T1, typename T2> class Test { public: void print(T1 i, T2 j) { cout <<i + j <<endl; } }; /* 类模板的局部特化,两个相同的参数相加 */ template <typename T> class Test<T,T> { public: void print(T i, T j) { cout << "print(T i, T j)" <<endl; cout <<static_cast<T>(i + j) <<endl; } }; template <typename T> class Test<T,int> { public: void print(T i, int j) { cout << "print(T i, int j)" <<endl; cout <<i + j <<endl; } }; /* 指针的局部特化 */ template <typename T1, typename T2> class Test<T1*,T2*> { public: void print(T1* i, T2* j) { cout << "print(T1* i, T2* j)" <<endl; } }; int main(int argc, char** argv) { /* t1会优先匹配到第三个模板<T,int> */ Test<double, int> t1; /* t2匹配到第二个和第三个模板,地位相等,都是局部特化,因此会报错 */ //Test<int, int> t2; /* 匹配到第二个模板 */ Test<long, long> t2; /* t1会优先匹配到第三个模板<T,int> */ Test<float, int> t3; /* t4会匹配到第一模板和第4个模板,会报错 */ //Test<int*, int*> t4; /* 匹配到第4个模板<T1*, T2*> */ Test<long*, int*> t4; long i = 0;0 int j = 0; t1.print(10.001,8); /* 会优先选择特化的类模板 */ t2.print(2, 3); t3.print(3,4); t4.print(&i, &j); return 0; }
输出结果如下:
1.3 为什么需要特化,而不重新定义新类?
1.1.3 特化和重新定义新类看上去本质没有区别,但是如果定义新类,那么将变成一个类模板一个新类,
使用的时候需要考虑就是使用类模板还是用新类
1.1.4 而特化可以统一方式使用类模板和特化类,编译器自动优先选择特化类
1.4 非类型模板参数
-函数模板和类模板的参数可以是普通数值
Source Example1.4: #include <iostream> /* run this program using the console pauser or add your own getch, system("pause") or input loop */ using namespace std; template <typename T, int N> void func() { T array[N] = {0}; for (int i = 0; i < N; i++) { array[i] = i + 1; cout<<array[i]<<" " ; } cout<<endl; } int main(int argc, char** argv) { func<int, 5>(); func<float, 10>(); return 0; }
1.5 非类型模板参数与特化
Source Example1.5(计算1加到某个自然数): #include <iostream> /* run this program using the console pauser or add your own getch, system("pause") or input loop */ using namespace std; template <int N> class Sum{ public: /* 变为真正意义的常量,放入符号表 */ static const int VALUE = Sum<N-1>::VALUE + N; }; template <> class Sum<1>{ public: /* 变为真正意义的常量,放入符号表 */ static const int VALUE = 1; }; int main(int argc, char** argv) { /* 编译时计算出来了 */ cout<<Sum<100>::VALUE<<endl; return 0; }
1.6 非类型模板参数的限制
1.6.1 变量不能作为模板参数
/* 会报错 */ int n = 100; cout<<Sum<n>::VALUE<<endl;
1.6.2 浮点数和类对象不能作为模板参数
1.6.3 全局指针不能作为模板参数
注意:编译器的推导是在编译阶段完成的。因此,编译器的推导必须依赖于特化类,否则推导过程无法结束。(递归思想)
二.工程问题
2.1 在实际工程中内存操作时bug的重要来源
2.2 C++将对内存交给程序员自由使用,因此
2.2.1 未及时释放,将产生内存泄漏
2.2.2 重复释放同一段内存,行为未知
2.2.3 使用越界,操作了不属于自己的内存
思考->怎样最大避开上述的使用问题?
a. 内存越界的问题常发生在数组的使用中.
解决方案:数组类
工程中,在非特殊情况下,要求开发者使用预先编写的数组类对象代替C语言中的原生数组
b. 内存泄漏和内存多次释放常发生于指针的使用过程中
解决方案:智能指针
工程中,要求开发者使用预先编写的智能指针对象代替C语言中的原生指针
工程中的智能指针是一个类模板
1. 通过构造函数接管申请的堆内存
2. 通过西沟函数确保堆内存被及时释放
3. 通过重载指针运算符*和->模拟指针的行为
4. 通过重载比较运算符==和!=模拟指针的比较