C++模板 Bertha 。 2022-03-19 13:30 241阅读 0赞 文一:[/images/20220319/2dfa5cca396940129714244edcacafad.png][http_www.cnblogs.com_CaiNiaoZJ_archive_2011_08_15_2138705.html] # [C++之模板][http_www.cnblogs.com_CaiNiaoZJ_archive_2011_08_15_2138705.html] # 有以下这样3个求加法的函数: [![复制代码][copycode.gif]][http_www.cnblogs.com_CaiNiaoZJ_archive_2011_08_15_2138705.html] 1 int Add(int x,int y) 2 { 3 return x+y; 4 } 5 6 double Add(double x,double y) 7 { 8 return x+y; 9 } 10 11 long Add(long x,long y) 12 { 13 return x+y; 14 } [![复制代码][copycode.gif]][http_www.cnblogs.com_CaiNiaoZJ_archive_2011_08_15_2138705.html] 它们拥有同一个函数名,相同的函数体,却因为参数类型和返回值类型不一样,所以是3个完全不同的函数。即使它们是二元加法的重载函数,但是不得不为每一函数编写一组函数体完全相同的代码。如果从这些函数中提炼出一个通用函数,而它又适用于多种不同类型的数据,这样会使代码的重用率大大提高。那么C++的模板就可解决这样的问题。模板可以实现类型的参数化(把类型定义为参数),从而实现了真正的代码可重用性。C++中的模板可分为函数模板和类模板,而把函数模板的具体化称为模板函数,把类模板的具体化成为模板类。 1.函数模板就是建立一个通用的函数,其参数类型和返回类型不具体指定,用一个虚拟的类型来代表。函数模板的声明格式: template<typename 类型参数> 返回类型 函数名(模板形参表) \{ 函数体 \} 或 template<class 类型参数> 返回类型 函数名(模板形参表) \{ 函数体 \} template是一个声明模板的关键字,类型参数一般用T这样的标识符来代表一个虚拟的类型,当使用函数模板时,会将类型参数具体化。typename和class关键字作用都是用来表示它们之后的参数是一个类型的参数。只不过class是早期C++版本中所使用的,后来为了不与类产生混淆,所以增加个关键字typename。下面我就对上述3个加法函数进行函数模板化: ![ExpandedBlockStart.gif][] [![复制代码][copycode.gif]][http_www.cnblogs.com_CaiNiaoZJ_archive_2011_08_15_2138705.html] 1 #include "stdafx.h" 2 #include <iostream> 3 4 template <typename T>//加法函数模板 5 T Add(T x,T y) 6 { 7 return x+y; 8 } 9 10 int main() 11 { 12 13 int x=10,y=10; 14 std::cout<<Add(x,y)<<std::endl;//相当于调用函数int Add(int,int) 15 16 double x1=10.10,y1=10.10; 17 std::cout<<Add(x1,y1)<<std::endl;//相当于调用函数double Add(double,double) 18 19 long x2=9999,y2=9999; 20 std::cout<<Add(x2,y2)<<std::endl;//相当于调用函数long Add(long,long) 21 22 return0; 23 } [![复制代码][copycode.gif]][http_www.cnblogs.com_CaiNiaoZJ_archive_2011_08_15_2138705.html] 结果: ![2011081508500368.jpg][] 看着上述的代码中,是否觉得函数模板的声明和C\#中的泛型使用有点相像呢,当调用函数模板时(如:Add(10,10))就是对函数模板的具体化(如:int Add(int,int)),具体化的函数模板就是模板函数。在函数模板中类型参数也可以指定多个,只不过定义的每个类型参数之前都必须有关键字typename(class)。 ![ExpandedBlockStart.gif][] [![复制代码][copycode.gif]][http_www.cnblogs.com_CaiNiaoZJ_archive_2011_08_15_2138705.html] 1 #include "stdafx.h" 2 #include <iostream> 3 4 template <typename T1,typename T2>//多类型参数的函数模板 5 T1 Add(T1 x,T2 y) 6 { 7 return x+y; 8 } 9 10 int main() 11 { 12 int x=10; 13 double y=10.10; 14 std::cout<<Add(x,y)<<std::endl;//相当于调用函数int Add(int,double) 15 std::cout<<Add(y,x)<<std::endl;//相当于调用函数double Add(double,int) 16 17 return0; 18 } [![复制代码][copycode.gif]][http_www.cnblogs.com_CaiNiaoZJ_archive_2011_08_15_2138705.html] 结果: ![2011081509173713.jpg][] 在定义函数模板时要注意的一点是在template语句和函数模板定义语句之间是不允许插入其他的语句的。和一般函数一样,函数模板也可以重载: ![ExpandedBlockStart.gif][] [![复制代码][copycode.gif]][http_www.cnblogs.com_CaiNiaoZJ_archive_2011_08_15_2138705.html] 1 #include "stdafx.h" 2 #include <iostream> 3 4 template <typename T>//加法函数模板 5 T Add(T x,T y)//二元 6 { 7 std::cout<<"调用模板函数:"; 8 return x+y; 9 } 10 11 template <typename T>//重载加法函数模板 12 T Add(T x,T y,T z)//三元 13 { 14 std::cout<<"调用重载模板函数:"; 15 return x+y+z; 16 } 17 18 19 int main_19() 20 { 21 double x1=10.10,y1=10.10; 22 std::cout<<Add(x1,y1)<<std::endl;//调用模板函数 23 //相当于调用函数double Add(double,double) 24 std::cout<<Add(x1,y1,y1)<<std::endl;//调用重载模板函数 25 //相当于调用函数double Add(double,double,double) 26 27 return0; 28 } [![复制代码][copycode.gif]][http_www.cnblogs.com_CaiNiaoZJ_archive_2011_08_15_2138705.html] 结果: ![2011081511390024.jpg][] 函数模板与同名非模板函数也可以重载。比如: ![ExpandedBlockStart.gif][] [![复制代码][copycode.gif]][http_www.cnblogs.com_CaiNiaoZJ_archive_2011_08_15_2138705.html] 1 #include "stdafx.h" 2 #include <iostream> 3 4 5 template <typename T>//加法函数模板 6 T Add(T x,T y) 7 { 8 std::cout<<"调用模板函数:"; 9 return x+y; 10 } 11 12 int Add(int x,int y) 13 { 14 std::cout<<"调用非模板函数:"; 15 return x+y; 16 } 17 18 19 int main() 20 { 21 int x=10,y=10; 22 std::cout<<Add(x,y)<<std::endl;//调用非模板函数 23 24 double x1=10.10,y1=10.10; 25 std::cout<<Add(x1,y1)<<std::endl;//调用模板函数 26 27 return0; 28 } [![复制代码][copycode.gif]][http_www.cnblogs.com_CaiNiaoZJ_archive_2011_08_15_2138705.html] 结果: ![2011081509310050.jpg][] 就如示例代码运行的结果表明的一样,当模板函数和同名的非模板函数重载时,首先寻找与参数类型完全匹配的非模板函数,找到了,则调用它,如果没找到,则寻找函数模板,找到后具体化函数模板,而后调用该模板函数。 2.和函数模板一样,类模板就是建立一个通用类,其数据成员的类型、成员函数的返回类型和参数类型都不具体指定,用一个虚拟类型来代表。当使用类模板建立对象时,系统会根据实参的类型来取代类模板中的虚拟类型从而实现不同类的功能。其定义格式为: template <typename 类型参数> class 类名 \{ 类成员声明 \} 或 template <class 类型参数> class 类名 \{ 类成员声明 \} 在类成员声明里,成员数据类型、成员函数的返回类型和参数类型前面需加上类型参数。在类模板中成员函数既可以定义在类模板内,也可以定义在类模板外,在定义类模板外时C++有这样的规定:需要在成员函数定义之前进行模板声明,且在成员函数名之前加上“类名<类型参数>::”: template <typename(class) 类型参数> 返回类型 类名<类型参数>::函数名(形参) \{ 函数体 \} 而类模板定义对象的形式: 类模板名<实际类型> 对象名; 类模板名<实际类型> 对象名(实参); 示例说明如下: ![ExpandedBlockStart.gif][] [![复制代码][copycode.gif]][http_www.cnblogs.com_CaiNiaoZJ_archive_2011_08_15_2138705.html] 1 #include "stdafx.h" 2 #include <iostream> 3 #include <string> 4 5 template <typename T>//在类模板定义之前,都需要加上模板声明 6 class BinaryOperation//二目运算类 7 { 8 private: 9 T x; 10 T y; 11 char op; 12 void add() 13 { 14 std::cout<<x<<op<<y<<"="<<x+y<<std::endl; 15 } 16 void sub() 17 { 18 std::cout<<x<<op<<y<<"="<<x-y<<std::endl; 19 } 20 void mul(); 21 void div(); 22 public: 23 BinaryOperation(T x,T y):x(x),y(y) 24 { 25 } 26 void determineOp(char op); 27 }; 28 29 30 //在类外定义成员函数: 31 //在成员函数定义之前进行模板声明, 32 //且在成员函数名之前加上"类名<类型参数>::" 33 template <typename T> 34 void BinaryOperation <typename T>::mul() 35 { 36 std::cout<<x<<op<<y<<"="<<x*y<<std::endl; 37 } 38 39 template <typename T> 40 void BinaryOperation <typename T>::div() 41 { 42 43 std::cout<<x<<op<<y<<"="<<x/y<<std::endl; 44 } 45 46 template <typename T> 47 void BinaryOperation <typename T>::determineOp(char op) 48 { 49 this->op=op; 50 switch(op) 51 { 52 case'+': 53 add(); 54 break; 55 case'-': 56 sub(); 57 break; 58 case'*': 59 mul(); 60 break; 61 case'/': 62 div(); 63 break; 64 default: 65 break; 66 } 67 } 68 69 int main() 70 { 71 72 BinaryOperation<int> op(10,10); 73 op.determineOp('+'); 74 op.determineOp('-'); 75 op.determineOp('*'); 76 op.determineOp('/'); 77 78 return0; 79 } [![复制代码][copycode.gif]][http_www.cnblogs.com_CaiNiaoZJ_archive_2011_08_15_2138705.html] 结果: ![2011081511134769.jpg][] 和函数模板一样,类模板也允许定义多个类型参数,这里不再一一举例了,有兴趣的朋友可以参考上述的示例代码尝试一下。 文二:[http://www.cnblogs.com/gaojun/archive/2010/09/10/1823354.html][http_www.cnblogs.com_gaojun_archive_2010_09_10_1823354.html] **1.** **模板的概念。** 我们已经学过**重载(Overloading)**,对重载函数而言,C++的检查机制能通过函数参数的不同及所属类的不同。正确的调用重载函数。例如,为求两个数的最大值,我们定义MAX()函数需要对不同的数据类型分别定义不同**重载(Overload)**版本。 //函数1. int max(int x,int y); \{return(x>y)?x:y ;\} //函数2. float max( float x,float y)\{ return (x>y)? x:y ;\} //函数3. double max(double x,double y) \{return (c>y)? x:y ;\} 但如果在主函数中,我们分别定义了 char a,b; 那么在执行max(a,b);时 程序就会出错,因为我们没有定义char类型的重载版本。 现在,我们再重新审视上述的max()函数,它们都具有同样的功能,即求两个数的最大值,能否只写一套代码解决这个问题呢?这样就会避免因重载函数定义不 全面而带来的调用错误。为解决上述问题C++引入模板机制,**模板定义:模板就是实现代码重用机制的一种工具,它可以实现类型参数化,即把类型定义为参数, 从而实现了真正的代码可重用性。模版可以分为两类,一个是函数模版,另外一个是类模版。** **2.函数模板的写法** 函数模板的一般形式如下: *Template <**class**或者也可以用**typename** T>* *返回类型 函数名(形参表) \{//函数定义体 \}* **说明: template是一个声明模板的关键字,表示声明一个模板关键字class不能省略,如果类型形参多余一个 ,每个形参前都要加class <类型 形参表>可以包含基本数据类型可以包含类类型.** 请看以下程序: **//Test.cpp** \#include<iostream> using std::cout; using std::endl; //声明一个函数模版,用来比较输入的两个相同数据类型的参数的大小,class也可以被typename代替, //T可以被任何字母或者数字代替。 template <class T> T min(T x,T y) \{ return(x<y)?x:y;\} void main( ) \{ int n1=2,n2=10; double d1=1.5,d2=5.6; cout<< "较小整数:"<<min(n1,n2)<<endl; cout<< "较小实数:"<<min(d1,d2)<<endl; system("PAUSE"); \} 程序运行结果: ![E5_87_BD_E6_95_B0_E6_A8_A1_E6_9D_BF_E8_BF_90_E8_A1_8C_E7_BB_93_E6_9E_9C.jpg][] 程序分析:main()函数中定义了两个整型变量n1 , n2 两个双精度类型变量d1 , d2然后调用min( n1, n2); 即实例化函数模板T min(T x, T y)其中T为int型,求出n1,n2中的最小值.同理调用min(d1,d2)时,求出d1,d2中的最小值. **3. 类模板的写法** 定义一个类模板: *Template < **class**或者也可以用**typename** T > class类名{ //类定义...... };* **说明:其中,template是声明各模板的关键字,表示声明一个模板,模板参数可以是一个,也可以是多个。** 例如:定义一个类模板: **// ClassTemplate.h** \#ifndef ClassTemplate\_HH \#define ClassTemplate\_HH template<typename T1,typename T2> class myClass\{ private: T1 I; T2 J; public: myClass(T1 a, T2 b);//Constructor void show(); \}; //这是构造函数 //注意这些格式 template <typename T1,typename T2> myClass<T1,T2>::myClass(T1 a,T2 b):I(a),J(b)\{\} //这是void show(); template <typename T1,typename T2> void myClass<T1,T2>::show() \{ cout<<"I="<<I<<", J="<<J<<endl; \} \#endif **// Test.cpp** \#include<iostream> \#include"ClassTemplate.h" using std::cout; using std::endl; void main() \{ myClass<int,int> class1(3,5); class1.show(); myClass<int,char> class2(3,'a'); class2.show(); myClass<double,int> class3(2.9,10); class3.show(); system("PAUSE"); \} 最后结果显示: **4.非类型模版参数** 一般来说,非类型模板参数可以是常整数(包括枚举)或者指向外部链接对象的指针。 那么就是说,浮点数是不行的,指向内部链接对象的指针是不行的。 template<typename T, int MAXSIZE> class Stack\{ Private: T elems\[MAXSIZE\]; … \}; Int main() \{ Stack<int, 20> int20Stack; Stack<int, 40> int40Stack; … \}; **5.使用模板类型** 有时模板类型是一个容器或类,要使用该类型下的类型可以直接调用,以下是一个可打印STL中顺序和链的容器的模板函数 template <typename T> void print(T v) \{ T::iterator itor; for (itor = v.begin(); itor != v.end(); ++itor) \{ cout << \*itor << " "; \} cout << endl; \} void main(int argc, char \*\*argv)\{ list<int> l; l.push\_back(1); l.push\_front(2); if(!l.empty()) print(l); vector<int> vec; vec.push\_back(1); vec.push\_back(6); if(!vec.empty()) print(vec); \} 打印结果 ![E4_BD_BF_E7_94_A8_E6_A8_A1_E6_9D_BF_E7_B1_BB_E5_9E_8B_E7_BB_93_E6_9E_9C.jpg][] 类型推导的隐式类型转换 在决定模板参数类型前,编译器执行下列隐式类型转换: 左值变换 修饰字转换 派生类到基类的转换 见《C++ Primer》(\[注2\],P500)对此主题的完备讨论。 简而言之,编译器削弱了某些类型属性,例如我们例子中的引用类型的左值属性。举例来说,编译器用值类型实例化函数模板,而不是用相应的引用类型。 同样地,它用指针类型实例化函数模板,而不是相应的数组类型。 它去除const修饰,绝不会用const类型实例化函数模板,总是用相应的非 const类型,不过对于指针来说,指针和 const 指针是不同的类型。 底线是:自动模板参数推导包含类型转换,并且在编译器自动决定模板参数时某些类型属性将丢失。这些类型属性可以在使用显式函数模板参数申明时得以保留。 **6. 模板的特化** 如果我们打算给模板函数(类)的某个特定类型写一个函数,就需要用到模板的特化,比如我们打算用 long 类型调用 max 的时候,返回小的值(原谅我举了不恰当的例子): template<> // 这代表了下面是一个模板函数 long max<long>( long a, long b ) // 对于 vc 来说,这里的 <long> 是可以省略的 \{ return a > b ? b : a; \} 实际上,所谓特化,就是代替编译器完成了对指定类型的特化工作,现代的模板库中,大量的使用了这个技巧。 对于偏特化,则只针对模板类型中部分类型进行特化,如 template<T1, T2> class MyClass; template<T1, T2> class MyCalss<int, T2>//偏特化 7. 仿函数 仿函数这个词经常会出现在模板库里(比如 STL),那么什么是仿函数呢? 顾名思义:仿函数就是能像函数一样工作的东西,请原谅我用东西这样一个代词,下面我会慢慢解释。 void dosome( int i ) 这个 dosome 是一个函数,我们可以这样来使用它: dosome(5); 那么,有什么东西可以像这样工作么? 答案1:重载了 () 操作符的对象,因此,这里需要明确两点: 1 仿函数不是函数,它是个类; 2 仿函数重载了()运算符,使得它的对你可以像函数那样子调用(代码的形式好像是在调用比如: struct DoSome \{ void operator()( int i ); \} DoSome dosome; 这里类(对 C++ 来说,struct 和类是相同的) 重载了 () 操作符,因此它的实例 dosome 可以这样用 dosome(5); 和上面的函数调用一模一样,不是么?所以 dosome 就是一个仿函数了。 实际上还有答案2: 函数指针指向的对象。 typedef void( \*DoSomePtr )( int ); typedef void( DoSome )( int ); DoSomePtr \*ptr=&func; DoSome& dosome=\*ptr; dosome(5); // 这里又和函数调用一模一样了。 当然,答案3 成员函数指针指向的成员函数就是意料之中的答案了。 8. 仿函数的用处 不管是对象还是函数指针等等,它们都是可以被作为参数传递,或者被作为变量保存的。因此我们就可以把一个仿函数传递给一个函数,由这个函数根据需要来调用这个仿函数(有点类似回调)。 STL 模板库中,大量使用了这种技巧,来实现库的“灵活”。 比如: for\_each, 它的源代码大致如下: template< typename Iterator, typename Functor > void for\_each( Iterator begin, Iterator end, Fucntor func ) \{ for( ; begin!=end; begin++ ) func( \*begin ); \} 这个 for 循环遍历了容器中的每一个元素,对每个元素调用了仿函数 func,这样就实现了 对“每个元素做同样的事”这样一种编程的思想。 特别的,如果仿函数是一个对象,这个对象是可以有成员变量的,这就让 仿函数有了“状态”,从而实现了更高的灵活性。 [http_www.cnblogs.com_CaiNiaoZJ_archive_2011_08_15_2138705.html]: /images/20220319/2dfa5cca396940129714244edcacafad.png [copycode.gif]: http://common.cnblogs.com/images/copycode.gif [ExpandedBlockStart.gif]: /images/20220319/9aea3c77a001418ca4ae1e142a149983.png [2011081508500368.jpg]: /images/20220319/43fd917b8d6a42c489c18d1c11886e1e.png [2011081509173713.jpg]: /images/20220319/d20dc3e32dcd453b84f89aa1aaa8072b.png [2011081511390024.jpg]: /images/20220319/16d4dac2fcfa49579748d32976df6233.png [2011081509310050.jpg]: /images/20220319/e7be00184b104969aeceaa85d041b27c.png [2011081511134769.jpg]: /images/20220319/67a3c61806a94ae3a01fd80fc102c763.png [http_www.cnblogs.com_gaojun_archive_2010_09_10_1823354.html]: http://www.cnblogs.com/gaojun/archive/2010/09/10/1823354.html [E5_87_BD_E6_95_B0_E6_A8_A1_E6_9D_BF_E8_BF_90_E8_A1_8C_E7_BB_93_E6_9E_9C.jpg]: /images/20220319/aba4c7feee2449d18d370e040a63f306.png [E4_BD_BF_E7_94_A8_E6_A8_A1_E6_9D_BF_E7_B1_BB_E5_9E_8B_E7_BB_93_E6_9E_9C.jpg]: /images/20220319/4ebbc4bbde404835beb3b977f8aa2313.png
相关 C++ 模板 C++入门系列文章: [C++、STL常用容器][C_STL] [C++、STL – 函数对象、常用算法][C_STL _] 前言: > 学习模板并不是为了写模板,而 冷不防/ 2024年03月25日 12:45/ 0 赞/ 78 阅读
相关 C++模板 C++模板 1. 模板概念 2. 函数模板 2.1 函数模板概念 2.2 函数模板格式 2.3 函数模板的原理 客官°小女子只卖身不卖艺/ 2023年09月28日 12:09/ 0 赞/ 19 阅读
相关 C++模板 1. 为什么要使用模板? 1. 假如设计一个求两参数最大值的函数,在实践中我们可能需要定义四个函数: ![format_png][] 2. 这些函数 怼烎@/ 2023年05月31日 06:56/ 0 赞/ 16 阅读
相关 C++ 模板: 函数模板 文章目录 C++ 模板 函数模板 1. 模板的概念 2. 函数模板 2.1 函数模板语法 2.2 秒速五厘米/ 2022年12月30日 12:55/ 0 赞/ 274 阅读
相关 C++模板 函数模板 函数模板,是可以创建一个通用的函数,可以支持多种形参。 用关键字 `template` 来定义, 在函数模板中,数据的值和类型都被参数化了,发生函数调用时编 你的名字/ 2022年12月23日 00:43/ 0 赞/ 125 阅读
相关 C++模板 C++模板 ①模板是实现代码重用机制的一种工具。就是根据参数类型生成函数和类的机制。它可以分成两类:一是函数模板,二是类模板,他们允许用户构造模板函数,模板类。 也可称通用 谁借莪1个温暖的怀抱¢/ 2022年09月17日 11:20/ 0 赞/ 148 阅读
相关 c++模板 1定义函数模板 include<stdexcept> include <sstream> include <map> using namesp 水深无声/ 2022年08月21日 08:55/ 0 赞/ 167 阅读
相关 C++:模板 http://[blog.csdn.net/pipisorry/article/details/72353250][blog.csdn.net_pipisorry_articl 逃离我推掉我的手/ 2022年06月16日 13:59/ 0 赞/ 198 阅读
相关 c++模板 1.类模板及其(全)特化和偏特化 模板特化是通过"给模板中的所有模板参数一个具体的类"的方式来实现的.而模板偏特化则是通过"给模板中的部分模板参数以具体的类,而留下剩余的模板 红太狼/ 2022年05月17日 03:49/ 0 赞/ 159 阅读
相关 C++模板 文一:[/images/20220319/2dfa5cca396940129714244edcacafad.png][http_www.cnblogs.com_CaiNiaoZ Bertha 。/ 2022年03月19日 13:30/ 0 赞/ 242 阅读
还没有评论,来说两句吧...