尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

C++初阶—模板初阶

发布时间:2026/9/29 7:40:20

资讯中心
01
ARTICLE

C++初阶—模板初阶

C++初阶—模板初阶
第一章泛型编程如何实现一个通用的交换函数呢void Swap(int left, int right) { int temp left; left right; right temp; } void Swap(double left, double right) { double temp left; left right; right temp; } void Swap(char left, char right) { char temp left; left right; right temp; }使用函数重载虽然可以实现但是有一下几个不好的地方重载的函数仅仅是类型不同代码复用率比较低只要有新类型出现时就需要用户自己增加对应的函数代码的可维护性比较低一个出错可能所有的重载均出错那能否告诉编译器一个模子让编译器根据不同的类型利用该模子来生成代码呢如果在C中也能够存在这样一个模具通过给这个模具中填充不同材料(类型)来获得不同材料的铸件(即生成具体类型的代码泛型编程编写与类型无关的通用代码是代码复用的一种手段。模板是泛型编程的基础。第二章函数模板2.1 函数模板概念函数模板代表了一个函数家族该函数模板与类型无关在使用时被参数化根据实参类型产生函数的特定类型版本。2.2 函数模板格式templatetypename T1, typename T2,......,typename Tn返回值类型 函数名(参数列表){}//template class T //template typename T //单参数 //template typename T1, typename T2//多参数。模版参数定义的是类型 //注意typename是用来定义模板参数关键字也可以使用class(切记不能使用struct代替class) //编译器用模版实例化生成对应的Swap函数 template typename T void Swap(T left, T right) { T temp left; left right; right temp; } int main() { int i 0, j 1; Swap(i, j); double x 1.1, y 2.2; Swap(x, y); //这两个Swap调用的并不是同一个函数 return 0; }注意typename是用来定义模板参数关键字也可以使用class(切记不能使用struct代替class)2.3 函数模板的原理函数模板是一个蓝图它本身并不是函数是编译器用使用方式产生特定具体类型函数的模具。所以其实模板就是将本来应该我们做的重复的事情交给了编译器在编译器编译阶段对于模板函数的使用编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如当用double类型使用函数模板时编译器通过对实参类型的推演将T确定为double类型然后产生一份专门处理double类型的代码对于字符类型也是如此。2.4 函数模板的实例化用不同类型的参数使用函数模板时称为函数模板的实例化。模板参数实例化分为隐式实例化和显式实例化。templateclass T T Add(const T left, const T right) { return left right; } int main() { //1. 隐式实例化让编译器根据实参推演模板参数的实际类型 cout Add(1, 2) endl; cout Add(1.1, 2.2) endl; //cout Add(1.1, 2) endl;//报错 //该语句不能通过编译因为在编译期间当编译器看到该实例化时需要推演其实参类型 //通过实参a1将T推演为int通过实参d1将T推演为double类型但模板参数列表中只有一个T //编译器无法确定此处到底该将T确定为int 或者 double类型而报错 //注意在模板中编译器一般不会进行类型转换操作因为一旦转化出问题编译器就需要背黑锅 //此时有两种处理方式1.用户自己来强制转化 2. 使用显式实例化 //解决方案一用户自己来强制转化 cout Add((int)1.1, 2) endl; cout Add(1.1, (double)2) endl; //2. 显式实例化在函数名后的中指定模板参数的实际类型 //解决方案二显示实例化 cout Addint(1.1, 2) endl; cout Adddouble(1.1, 2) endl; return 0; }如果类型不匹配编译器会尝试进行隐式类型转换如果无法转换成功编译器将会报错。为什么要有显示实例化//下方示例没有显示实例化编译器无法推导类型 template class T T* func(int n) { return new T[n]; } int main() { //无法调用无法推导。10只是传递给n用于指定数组的大小。 //但是T 是什么类型编译器无从得知因为 n 和 T 没有直接关联。 //可能会想ptr 是 int*那 func(10) 不是返回 int* 吗为什么不能反推 Tint 呢 //原因C 的模板推导只根据函数参数进行不能根据返回值进行推导。 //编译器的工作方式 //1.编译阶段当遇到 func(10)编译器需要先确定 T 是什么类型然后实例化 funcT。 //2.但是 10 无法提供 T 的信息编译器只能看到 func(n)而 n 只是 int跟 T 没有直接关系。 //3.返回值类型在调用时才起作用但在模板推导阶段C 不会反向推导 T。 //int* ptr func(10); int* ptr funcint(10); return 0; }2.5 模板参数的匹配原则1. 一个非模板函数可以和一个同名的函数模板同时存在而且该函数模板还可以被实例化为这个非模板函数templateclass T T Add(T left, T right) { return left right; } int Add(int a, int b) { return a b; } int main() { int x 0, y 1; swap(x, y);//C库提供了交换函数模版 double a 1.1, b 2.2; swap(a, b); cout Add(x, y) endl;//调用的第二个函数与非模板函数匹配编译器不需要特化 cout Addint(x, y) endl;//指定调用模板调用编译器特化的Add版本 return 0; }2. 对于非模板函数和同名函数模板如果其他条件都相同在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数 那么将选择模板// 专门处理int的加法函数 int Add(int left, int right) { return left right; } // 通用加法函数 templateclass T1, class T2 T1 Add(T1 left, T2 right) { return left right; } void Test() { Add(1, 2); // 与非函数模板类型完全匹配不需要函数模板实例化 Add(1, 2.0); // 模板函数可以生成更加匹配的版本编译器根据实参生成更加匹配的Add函数 }3. 模板函数不允许自动类型转换但普通函数可以进行自动类型转换第三章类模板3.1 类模板的定义格式templateclass T1, class T2, ..., class Tn class 类模板名 { // 类内成员定义 };栈示例typedef int DataType; class Stack { public: Stack(size_t capacity 4) { cout Stack() endl; _array new DataType[capacity]; _capacity capacity; _size 0; } void Push(DataType data) { // CheckCapacity(); _array[_size] data; _size; } ~Stack() { cout ~Stack() endl; delete[] _array; _array nullptr; _size _capacity 0; } private: // 内置类型 DataType* _array; int _capacity; int _size; }; int main() { //typedef虽然可以解决栈类换数据类型 //但如果下方需要st1存intst2存double就不能很好解决 //因为又会回到类似交换函数的问题只是更改typedef的类型和栈的名字。其余部分都相同 Stack st1;//int Stack st2;//double return 0; }解决方法使用类模板3.2 类模板的实例化类模板实例化与函数模板实例化不同类模板实例化需要在类模板名字后跟然后将实例化的类型放在中即可类模板名字不是真正的类而实例化的结果才是真正的类。template class T class Stack { public: Stack(size_t capacity 4) { cout Stack() endl; _array new T[capacity]; _capacity capacity; _size 0; } void Push(const T data) { //待插入的数据不需要修改且传引用增加效率 // CheckCapacity(); _array[_size] data; _size; } ~Stack() { cout ~Stack() endl; delete[] _array; _array nullptr; _size _capacity 0; } private: // 内置类型 T* _array; int _capacity; int _size; }; int main() { //这里编译器无法推断类型所以需要显示实例化 Stackint st1; Stackdouble st2; //上方依然是2个类 st1.Push(1); st1.Push(2); st2.Push(1.1); st2.Push(2.2); return 0; }动态顺序表templateclass T class Vector { public: Vector(size_t capacity 10) : _pData(new T[capacity]) , _size(0) , _capacity(capacity) { } // 使用析构函数演示在类中声明在类外定义。 ~Vector(); void PushBack(const T data); void PopBack(); // ... size_t Size() { return _size; } T operator[](size_t pos) { assert(pos _size); return _pData[pos]; } private: T* _pData; size_t _size; size_t _capacity; }; //之前的类名就是类型但类模版的类名不是类型 //类模版 类名Vector //类模版 类型VectorT //类上方的template可以用的范围是类里面所以声明和定义分离还要在单独声明模版参数 templateclass T VectorT::~Vector() { delete[] _pData; _pData mullptr; } int main() { Vectorint v; return 0; }作业1. 下列描述错误的是 A.编写与类型无关的通用代码是代码复用的一种手段。模板是泛型编程的基础B.函数模板是一个蓝图它本身并不是函数是编译器用使用方式产生特定具体类型函数的模具C.模板分为函数模板和类模板D. 模板类跟普通类以一样的编译器对它的处理时一样的答案DA.模板是代码复用的重要手段B.函数模板不是一个具体函数而是一个函数家族C.目前涉及到的模板就两类函数模板与类模板D.模板类是一个家族编译器的处理会分别进行两次编译其处理过程跟普通类不一样2. 下列的模板声明中其中几个是正确的 1)template2)templateT1,T23)templateclass T1,T24)templateclass T1,class T25)templatetypename T1,T26)templatetypename T1,typename T27)templateclass T1,typename T28)typename T1,class T2A.2B.3C.4D.5答案BA.1.模板语法错误2.没有关键字class或typename指定类型3.T2缺少class或typenameB.正确 4,6,7为正确声明C.5.T2缺少class或typenameD.8.缺少template3. 下列关于模板的说法正确的是 A.模板的实参在任何时候都可以省略B.类模板与模板类所指的是同一概念C.类模板的参数必须是虚拟类型的D.类模板中的成员函数全是模板函数答案DA. 模板实参省略意思为隐式实例化一般情况下都使用隐式实例化不需指定模板类型参数让编译器进行推导但有些情况下编译器推导时可能会有歧义比如模板参数只有一个类型T但是用两个不同类型隐式实例化templateclass T T Add(const T x, const T y) { return x y; } int main() { Add(10, 20); // 正确 Add(1.2, 30); // 编译失败 改正 Addint(1.2, 20) 或者 Add((int)1.2, 20) return 0; }所以模板参数不是任何情况下都可以省略要结合具体的使用场景因此A的说法是错误的。B.类模板是一个类家族模板类是通过类模板实例化的具体类C.C中类模板的参数即为模板参数列表中内容有两种方式类型参数和非类型参数类型参数即类型参数化将来实例化为具体的实际类型有点像函数的形参形参可以接受不同值的实参非类型参数在定义时给定了具体的类型用该类型定义的为常量比如templateclass T, size_t N class array { // ... };D.正确定义时都必须通过完整的模板语法进行定义。 因为所有类模板的成员函数放在类外定义时需要在函数名前加类名而类名实际ClassNameT所以定义时还需加模板参数列表templateclass T size_t StackT::size() { return _size; }因此类模板中的成员函数都是函数模板D正确。4. 在下列对fun的调用中错误的是 template class T T fun(T x, T y) { return x * x y * y; }A.fun(1, 2)B.fun(1.0, 2)C.fun(2.0, 1.0)D.funfloat(1, 2.0)答案BA.通过参数推导T为int,不存在二义性调用正确B.由于参数类型不一样模板不支持类型转换推导参数会产生二义性编译错误C.通过参数推导T为float不存在二义性调用正确D.通过类型实例化函数调用正确5. 下面有关C中为什么用模板类的原因描述错误的是? ( A.可用来创建动态增长和减小的数据结构B.它是类型无关的因此具有很高的可复用性C.它运行时检查数据类型保证了类型安全D.它是平台无关的可移植性答案CA.模板可以具有非类型参数用于指定大小可以根据指定的大小创建动态结构B.模板最重要的一点就是类型无关提高了代码复用性C.模板运行时不检查数据类型也不保证类型安全相当于类型的宏替换故错误D.只要支持模板语法模板的代码就是可移植的
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。