文章目录
- 类模板的局部特化
- 类模板可以定义多个类型参数
- 类模板可以被局部特化
- 思考:为什么需要特化,而不重新定义新类
- 非类型模板参数
- 函数模板的模板参数可以是普通数值
- 代码实例 非类型模板参数与特化
- 工程问题:
- 内存越界的问题常发生于数组的使用中
- 内存泄漏和内存多次释放常发生于指针的使用过程中
- 智能指针
- 代码实例 智能指针的创建与使用
- 小结:
类模板的局部特化
类模板可以定义多个类型参数
#include <cstdlib> #include <iostream> using namespace std; template<typename T1, typename T2> class Test { public: void test(T1 t1, T2 t2){ cout << t1+t2 <<endl; } }; int main() { Test<double, int> t; t.test(10.001, 5); return 0; }类模板可以被局部特化
可以指定类模板的特定实现,并要求某些类型参数仍然必须得模板的用户指定
#include <cstdlib> #include <iostream> using namespace std; template<typename T1, typename T2> class Test { public: void add(T1 a, T2 b) { cout<<(a + b)<<endl; } }; /* template<typename T> class Test<T, T> { public: void add(T a, T b) { cout<<"add(T a, T b)"<<endl; cout<<static_cast<T>(a + b)<<endl; } }; */ template<typename T> class Test<T, int> { public: void add(T a, int b) { cout<<"add(T a, int b)"<<endl; cout<<a + b<<endl; } }; template<typename T1, typename T2> class Test<T1*, T2*> { public: void add(T1* a, T2* b) { cout<<"add(T1* a, T2* b)"<<endl; //两个指针相加是没有意义的,这也就是为什么要特化的原因!!! } }; int main(int argc, char *argv[]) { int i = 0; int j = 0; Test<double, int> t; // <T, int> Test<long, long> ti; // <T1, T2> Test<float, int> tt; // <T, int> Test<int*, int*> tp; // <T*, T*> t.add(10.0001, 8); ti.add(2, 3); tt.add(4, 5); tp.add(&i, &j); cout << "Press the enter key to continue ..."; cin.get(); return EXIT_SUCCESS; }思考:为什么需要特化,而不重新定义新类
特化和重新定义新类看上去没有本质区别,但是如果定义新类,那么将变成一个类模板和一个新类,使用的时候需要考虑究竟是用类模板还是用新类
而特化可以统一的方式使用类模板和特化类,编译器自动优先选择特化类
注意一下:
主模版,完全特化,局部特化
这3者的语法区别:
非类型模板参数
函数模板和类模板的模板参数可以是普通数值
函数模板的模板参数可以是普通数值
#include <cstdlib> #include <iostream> 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>(); cout << "Press the enter key to continue ..."; cin.get(); return EXIT_SUCCESS; }一道面试题:怎么求 前N项和 最快?
代码实例 非类型模板参数与特化
#include <cstdlib> #include <iostream> using namespace std; template<int N> class Sum { public: static const int VALUE = Sum<N - 1>::VALUE + N; //小知识点:VALUE被放入符号表; static修饰在全局数据区; 不会给VALUE分配空间 //递归的思想 //编译器 编译的时候 计算 }; //特化类 template<> class Sum<1> //递归的出口 { public: static const int VALUE = 1; }; int main(int argc, char *argv[]) { cout<<Sum<10>::VALUE<<endl; //编译器二次编译 cout<<Sum<100>::VALUE<<endl; cout << "Press the enter key to continue ..."; cin.get(); return EXIT_SUCCESS; }非类型模板参数的限制
变量不能作为模板参数
intn=10;cout<<Sum<n>::VALUE<<endl;//error 浮点数和类对象不能作为模板参数
cout<<Sum<10.001>::VALUE<<endl;//error 全局指针不能作为模板参数
tips:
编译器的推导过程是在编译阶段完成的。因此, 编译器的推导必须依赖于特化类,否则推导过程 无法结束。(递归)
工程问题:
在实际工程中内存操作是bug的重要来源
C++将堆内存交由程序员自由使用,因此
未及时释放,将产生内存泄漏
重复释放同一段内存,行为未知
使用越界,操作了不属于自己的内存
思考:怎样最大限度的避开上述的使用问题?
内存越界的问题常发生于数组的使用中
解决方案:数组类
工程中,在非特殊情况下,要求开发者使用预先编写的数组类对象代替C语言中的原生数组
内存泄漏和内存多次释放常发生于指针的使用过程中
解决方案:智能指针
工程中,要求开发者使用预先编写的智能指针类对象代替C语言中的原生指针
什么是智能指针?
智能指针
工程中的智能指针是一个类模板
通过构造函数接管申请的堆内存
通过析构函数确保堆内存被及时释放
通过重载指针运算符 * 和 -> 模拟指针的行为
通过重载比较运算符 – 和 != 模拟指针的比较
代码实例 智能指针的创建与使用
SmartPointer.h
#ifndef _SMARTPOINTER_H_ #define _SMARTPOINTER_H_ template<typename T> class SmartPointer { protected: T* m_pointer; public: SmartPointer(); SmartPointer(const T* pointer); ~SmartPointer(); T* operator->(); T& operator*(); }; #endifSmartPointer.hpp
#ifndef _SMARTPOINTER_DEF_H_ #define _SMARTPOINTER_DEF_H_ #include "SmartPointer.h" template<typename T> SmartPointer<T>::SmartPointer() //无参构造函数:初始化为NULL { m_pointer = NULL; } template<typename T> SmartPointer<T>::SmartPointer(const T* pointer) //通过构造函数接管申请的堆内存 { m_pointer = const_cast<T*>(pointer); } template<typename T> SmartPointer<T>::~SmartPointer() //通过析构函数确保堆内存被及时释放 { delete m_pointer; } template<typename T> T* SmartPointer<T>::operator->() //重载 * 运算符 { return m_pointer; } template<typename T> T& SmartPointer<T>::operator*() //重载 -> 运算符 { return *m_pointer; } #endifmain.cpp
#include <cstdlib> #include <iostream> #include "SmartPointer.hpp" using namespace std; class Test //测试->类 { public: int i; void print() { cout<<i<<endl; } }; int main(int argc, char *argv[]) { SmartPointer<int> pi = new int(5); // <==> //SmartPointer<int> pi(new int(5) ); SmartPointer<Test> pt = new Test(); cout<<*pi<<endl; //*sp <==> sp.operator* () *pi = 100; cout<<*pi<<endl; pt->i = 20; //pt-> <==> pt.operator-> () pt->print(); cout << "Press the enter key to continue ..."; cin.get(); return EXIT_SUCCESS; }小结:
类模板中可以有一个或多个未指定的泛指类型
可以在需要的特化类模板
特化可以统一的方式使用类模板和新定义的类
特化类总是被编译器优先选择使用
模板的参数可以是普通数值
数组类和智能指针可以最大限度的避免内存相关的bug