尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

深入理解C++系列(12)——模版进阶

深入理解C++系列(12)——模版进阶 文章目录上期回顾模板进阶1. 非类型模板参数array类不如vector2. 模板的特化2.1 概念2.2 函数模板特化2.3 类模板特化2.3.1 全特化2.3.2 偏特化2.3.3 类模板特化应用示例3 模板分离编译3.1 什么是分离编译3.2 模板的分离编译3.3 解决方法4. 模板总结【优点】【缺陷】总结与回顾三、总结与回顾一、模板分类二、非类型模板参数三、array 与 vector 的区别四、模板特化模板特化最重要的结论四、模板分离编译下期预告结语◆ 博主此生决int分享编程知识深挖底层原理持续原创更新热门专栏深入理解 C系列|算法系列快速复习系列 |Java 速通系列 本系列知识点前后关联较强建议按照专栏顺序阅读哦本系列主要面向的阅读人群1学完 C 语言准备系统学习 C 的同学2已经学习过 C希望重新梳理知识体系的同学3想深入理解 C 设计思想与底层原理而不仅仅停留在语法层面的同学速胜派就是最大的投降派上期回顾栈和队列以及优先级队列模板进阶模版说到底就是一段代码有个地方你可以是int可以是double可以是string什么类型都可以那么我就直接抽象出一个符号来代替他这就是模版之后再实例化出具体的类来。1. 非类型模板参数模板参数分类类型形参与非类型形参。类型形参出现在模板参数列表中跟在class或者typename之类的参数类型名称。就是我们之前使用的TRefPtr等等他们代替的一般就是类型比如int,double,string等等非类型形参就是用一个常量作为类函数模板的一个参数在类函数模板中可将该参数当成常量来使用。如果写算法题就会知道一般会有这么一句代码constintN1e510;这里的N就是定义一个常量方便后面数组开辟等等那么我们这个非类型模版参数也具有这个作用并且更加厉害他可以根据你对象的不同需求而变化namespacebite{// 定义一个模板类型的静态数组templateclassT,size_t N10classarray{public:Toperator[](size_t index){return_array[index];}constToperator[](size_t index)const{return_array[index];}size_tsize()const{return_size;}boolempty()const{return0_size;}private:T _array[N];size_t _size;};}那么我们使用的时候arraychar,10a1//N就是10arrayint,20a2;//N就是20每个对象都不一样注意非类型模版参数一般都是整形特别的doubleC20浮点数、类对象以及字符串是不允许作为非类型模板参数的。非类型的模板参数必须在编译期就能确认结果。array类不如vectorSTL 为什么不用普通数组而自己封装一个 array因为静态数组对于边界的检查不够安全对于只读不检查对于只写只做抽查即只检查数组越界的头两个有没有被修改例如下面的代码inta3[10];// 静态数组抽查// 越界读不检查couta3[10]endl;//✅️//越界写抽查只抽前几个a3[10]10;//报错❌️a3[11]10;//报错❌️a3[12]10;//✅️a3[20]10;//✅️但是对于array类因为重载了operate[]所以会报断言错误assert但是依旧没什么用我完全可以用vector但他与vector的区别array存在栈上vector存在堆上2. 模板的特化2.1 概念简单来说就是模版不是把一些相同的只有类型不同的代码里面的类型提取出来抽象成了模版但是有的时候有些类型不太适合这个模版那么我们就需要对他进行特殊化处理——模版的特化。通常情况下使用模板可以实现一些与类型无关的代码但对于一些特殊类型的可能会得到一些错误的结果需要特殊处理比如实现了一个专门用来进行小于比较的函数模板。// 函数模板 -- 参数匹配templateclassTboolLess(T left,T right){returnleftright;}intmain(){coutLess(1,2)endl;// 可以比较结果正确Dated1(2022,7,7);Dated2(2022,7,8);coutLess(d1,d2)endl;// 可以比较结果正确Date*p1d1;Date*p2d2;coutLess(p1,p2)endl;// 可以比较结果错误return0;}对于一般的类型可以正常比较包括data类重载了等运算符但是对于指针类型他就是直接按照指针大小来进行比较这不是我们想要的所以我们需要对这个模版进行特化即在原模板类的基础上针对特殊类型所进行特殊化的实现方式。模板特化中分为函数模板特化与类模板特化。一定是要在原模版的基础上2.2 函数模板特化函数模板的特化步骤必须要先有一个基础的函数模板。关键字template后面接一对空的尖括号。函数名后跟一对尖括号尖括号中指定需要特化的类型。函数形参必须要和模板函数的基础参数类型完全相同如果不同编译器可能会报一些奇怪的错误。代码示例// 函数模板 -- 参数匹配templateclassTboolLess(T left,T right){returnleftright;}// 对 Less 函数模板进行特化templateboolLessDate*(Date*left,Date*right){return*left*right;}这样还不如直接写一个函数有函数的话就是更加匹配所以会直接调函数boolLess(Date*left,Date*right){return*left*right;}该种实现简单明了代码的可读性高容易书写因为对于一些参数类型复杂的函数模板特化时特别给出。因此函数模板不建议特化。不建议但我觉得还是可以写的2.3 类模板特化2.3.1 全特化全特化即是将模板参数列表中所有的参数都确定化。templateclassT1,classT2classData{public:Data(){coutDataT1, T2endl;}private:T1 _d1;T2 _d2;};templateclassDataint,char{public:Data(){coutDataint, charendl;}private:int_d1;char_d2;};voidTestVector(){Dataint,intd1;Dataint,chard2;}2.3.2 偏特化偏特化任何针对模板参数进一步进行条件限制设计的特化版本。比如对于以下模板类templateclassT1,classT2classData{public:Data(){coutDataT1, T2endl;}private:T1 _d1;T2 _d2;};偏特化有以下两种表现方式部分特化将模板参数类表中的一部分参数特化。// 将第二个参数特化为 inttemplateclassT1classDataT1,int{public:Data(){coutDataT1, intendl;}private:T1 _d1;int_d2;};参数更进一步的限制// 两个参数偏特化为指针类型templatetypenameT1,typenameT2classDataT1*,T2*{public:Data(){coutDataT1*, T2*endl;}private:T1 _d1;T2 _d2;};// 两个参数偏特化为引用类型templatetypenameT1,typenameT2classDataT1,T2{public:Data(constT1d1,constT2d2)...private:constT1_d1;constT2_d2;};voidtest2(){Datadouble,intd1;// 调用特化的 int 版本Dataint,doubled2;// 调用基础的模板Dataint*,int*d3;// 调用特化的指针版本Dataint,intd4(1,2);// 调用特化的指针版本}2.3.3 类模板特化应用示例有如下专门用来按照小于比较的类模板Less#includevector#includealgorithmtemplateclassTstructLess{booloperator()(constTx,constTy)const{returnxy;}};intmain(){Dated1(2022,7,7);Dated2(2022,7,6);Dated3(2022,7,8);vectorDatev1;v1.push_back(d1);v1.push_back(d2);v1.push_back(d3);// 可以直接排序结果是日期升序sort(v1.begin(),v1.end(),LessDate());vectorDate*v2;v2.push_back(d1);v2.push_back(d2);v2.push_back(d3);// 可以直接排序结果错误日期还不是升序而 v2 中放的地址是升序// 此处需要在排序过程中让 sort 比较 v2 中存放地址指向的日期对象// 但是 Less 模板sort 在排序时实际比较的是 v2 中指针的地址因此无法达到预期sort(v2.begin(),v2.end(),LessDate*());return0;}通过观察上述程序的结果发现对于日期对象可以直接排序并且结果是正确的。但是如果待排序元素是指针结果就不一定正确。因为sort最终按照Less模板中方式比较所以会比较指针而不是比较指针指向空间中内容此时可以使用类本身特化来处理上述问题// 对 Less 类模板按照指针方式特化templatestructLessDate*{booloperator()(Date*x,Date*y)const{return*x*y;}};特化之后在运行上述代码就可以得到正确的结果。甚至可以直接把所以指针比较都改了// 对 Less 类模板按照指针方式特化templatetypenameT1,typenameT2classDataT1*,T2*{booloperator()(T1*x,T2*y)const{return*x*y;}};3 模板分离编译我们来回顾一下.cpp变成.exe的过程3.1 什么是分离编译一个程序项目由若干个源文件共同实现而每个源文件单独编译生成目标文件最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。3.2 模板的分离编译假如有以下场景模板的声明与定义分离在头文件中进行声明源文件中完成定义// a.htemplateclassTTAdd(constTleft,constTright);// a.cpptemplateclassTTAdd(constTleft,constTright){returnleftright;}// main.cpp#includea.hintmain(){Add(1,2);Add(1.0,2.0);return0;}分析C/C 程序要运行一般要经历以下步骤预处理 —— 编译 —— 汇编 —— 链接编译对程序按照语言特性进行词法、语法、语义分析错误检查无误后生成汇编代码。注意头文件不参与编译编译器对工程中的多个源文件是分开单独编译的。链接将多个目标文件合并在一块并处理没有解决的地址问题。图片说明3.3 解决方法将声明和定义放到一个文件xxx.hpp里面或者xxx.h其实也是可以的。推荐使用这种。模板定义的位置显式实例化。这种方法不实用不推荐使用。【分离编译扩展阅读】http://blog.csdn.net/pongba/article/details/191304. 模板总结【优点】模板复用了代码节省资源更快的迭代开发C 的标准模板库STL因此而产生。增强了代码的灵活性。【缺陷】模板会导致代码膨胀问题也会导致编译时间变长。出现模板编译错误时错误信息非常凌乱不易定位错误。总结与回顾你这个模板的总结风格是**“考前速查表”**特点是只写结论不写原因。按知识点分类而不是按章节分类。重点放在记什么而不是为什么。尽量一眼扫完适合最后复习。按照你 list 那种风格模板这一章应该这样写三、总结与回顾一、模板分类模板就是把类型抽象出来实现代码复用。模板分为两种函数模板、类模板模板参数又分为类型模板参数、非类型模板参数二、非类型模板参数非类型模板参数就是编译期常量。例如templateclassT,size_t N10classarray{T _a[N];};特点一般使用整型常量如 int、size_t必须在编译期间确定不同对象可以实例化出不同的参数值三、array 与 vector 的区别arrayvector静态数组动态数组大小固定自动扩容数据一般位于栈上数据位于堆上不支持扩容支持扩容四、模板特化模板特化分为函数模板特化、类模板特化类模板特化又分为全特化、偏特化模板特化就是针对特殊类型提供特殊实现。模板特化最重要的结论函数模板一般不建议特化更推荐直接写普通函数。类模板特化在 STL 中使用非常广泛。全特化所有模板参数全部确定。偏特化只限定部分模板参数。偏特化既可以固定部分参数也可以限制参数类型如指针、引用。四、模板分离编译声明和定义放到同一个头文件.hpp/.h显式实例化很少使用不推荐下期预告继承结语本文到此结束感谢大家的阅读如果觉得本文对你有所帮助欢迎点赞、收藏、关注也欢迎在评论区一起交流讨论。也欢迎订阅我的深入理解 C系列从语法入门到底层原理系统掌握现代 C算法系列从入门到精通蓝桥杯、ACM、LeetCode 与面试算法全路线快速复习系列知识梳理、查漏补缺考前冲刺必备Java 速通系列已学 C 语言快速上手 Java轻松备战期末考试愿每一次敲下键盘都比昨天更进一步愿每一行代码落下都让未来多一种可能
返回列表