拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

类模板(下)

类模板(下)

文章目录

  • 类模板的局部特化
    • 类模板可以定义多个类型参数
    • 类模板可以被局部特化
    • 思考:为什么需要特化,而不重新定义新类
  • 非类型模板参数
    • 函数模板的模板参数可以是普通数值
    • 代码实例 非类型模板参数与特化
  • 工程问题:
    • 内存越界的问题常发生于数组的使用中
    • 内存泄漏和内存多次释放常发生于指针的使用过程中
  • 智能指针
    • 代码实例 智能指针的创建与使用
  • 小结:

类模板的局部特化

类模板可以定义多个类型参数

#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*(); }; #endif

SmartPointer.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; } #endif

main.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

返回列表