C++模板

来源:互联网 发布:java.equal例子 编辑:程序博客网 时间:2024/06/10 16:38

要写一个通用函数,一般有以下几个方法:
1.使用函数重载,针对每个所需相同行为的不同类型重新实现它
【缺点】
1、只要有新类型出现,就要重新添加对应函数。
2、除类型外,所有函数的函数体都相同,代码的复用率不高
3、如果函数只是返回值类型不同,函数重载不能解决
4、一个方法有问题,所有的方法都有问题,不好维护。
2.使用公共基类,将通用的代码放在公共的基础类里面
【缺点】
1、借助公共基类来编写通用代码,将失去类型检查的优点;
2、对于以后实现的许多类,都必须继承自某个特定的基类,代码维护更加困难。
3.使用特殊的预处理程序 即define 宏
【缺点】
不是函数,不进行参数类型检测,安全性不高
4.使用模板,泛型编程
这也是目前广泛使用的方法;

函数模板: 代表了一个函数家族,该函数与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
模板函数的格式

template <typename Param1, typename Param2,...,class Paramn>返回值类型 函数名(参数列表){...}例:template <  typename  T >T ADD ( T t1 ,  T  t2  ){...;}

typename是用来定义模板参数关键字,也可以使用class。建议尽量使用typename。
注意:不能使用struct代替typename。

模板函数也可以定义为inline函数

template<typename T>inline T Add(const T _left, const T _right){return (_left + _right);}

注意:inline关键字必须放在模板形参表之后,返回值之前,不能放在template之前

模板是一个蓝图,它本身不是类或者函数, 编译器用模板产生指定的类或者函数的特定类型版本,产生模板特定类型的过程称为函数模板实例化。

实例化之前,检查模板代码本身,查看是否出现语法错误,如:遗漏分号
在实例化期间,检查模板代码,查看是否所有的调用都有效,如:实例化类型不支持某些函数调用.
【实参推演】
从函数实参确定模板形参类型和值的过程称为模板实参推断,多个类型形参的实参必须完全匹配

【类型形参转换】
一般不会转换实参以匹配已有的实例化,相反会产生新的实例。
编译器只会执行两种转换:
1、const转换:接收const引用或者const指针的函数可以分别用非const对象的引用或者指针来调用
2、数组或函数到指针的转换:如果模板形参不是引用类型,则对数组或函数类型的实参应用常规指
针转换。数组实参将当做指向其第一个元素的指针,函数实参当做指向函数类型的指针。

函数模板 有两种类型参数:模板参数和调用参数;
模板形参名字只能在模板形参之后到模板声明或定义的末尾之间使用,遵循名字屏蔽规则
模板形参的名字在同一模板形参列表中只能使用一次
所有模板形参前面必须加上class或者typename关键字修饰
注意:在函数模板的内部不能指定缺省的模板实参。

【非模板类型参数】
非模板类型形参是模板内部定义的常量,在需要常量表达式的时候,可以使用非模板类型参数。
例 template

template<class 形参名1, class 形参名2, ...class 形参名n >class 类名{ ... };template<typename T>class SeqList{private :T* _data ;int _size ;int _capacity ;};// 以模板方式实现动态顺序表template<typename T>class SeqList{public :SeqList();~ SeqList();private :int _size ;int _capacity ;T* _data ;};template <typename T>SeqList <T >:: SeqList(): _size(0), _capacity(10), _data( new T[ _capacity]){}template <typename T>SeqList <T >::~ SeqList(){delete [] _data ;}void test1 (){SeqList<int > sl1;SeqList<double > sl2;}

【模板类的实例化】
只要有一种不同的类型,编译器就会实例化出一个对应的类。
SeqList sl1;
SeqList sl2;
当定义上述两种类型的顺序表时,编译器会使用int和double分别代替模板形参,重新编写
SeqList类,最后创建名为SeqList和SeqList的类。

模板的模板参数–容器适配器

template <typename T>class SeqList{private :int _size ;int _capacity ;T* _data ;};// template <class T, template<class> class Container>template <class T, template <class> class Container = SeqList> // 缺省参数class Stack{public :void Push( const T& x );void Pop();const T& Top();bool Empty();private :Container<T > _con;};void Test(){Stack<int > s1;Stack<int , SeqList> s2;}非类型的类模板参数// 静态顺序表//template<typename T, size_t MAX_SIZE>template <typename T, size_t MAX_SIZE = 10> //带缺省模板参数注意:浮点数和类对象是不允许作为非类型模板参数的类模板的特化全特化template <typename T>class SeqList{public :SeqList();~ SeqList();private :int _size ;int _capacity ;T* _data ;};template<typename T>SeqList <T >:: SeqList(): _size(0), _capacity(10), _data( new T[ _capacity]){cout<<"SeqList<T>" <<endl;}template<typename T>SeqList <T >::~ SeqList(){delete[] _data ;}template <>class SeqList < int>{public :SeqList(int capacity);~ SeqList();private :int _size ;int _capacity ;int* _data ;};// 特化后定义成员函数不再需要模板形参SeqList <int >:: SeqList( int capacity): _size(0), _capacity( capacity ), _data( new int[ _capacity ]){cout<<"SeqList<int>" <<endl;}// 特化后定义成员函数不再需要模板形参SeqList <int >::~ SeqList(){delete[] _data ;}void test1 (){SeqList<double > sl2;SeqList<int > sl1(2);}偏特化(局部特化)template <typename T1, typename T2>class Data{public :Data();private :T1 _d1 ;T2 _d2 ;};template <typename T1, typename T2>Data<T1 , T2>:: Data(){cout<<"Data<T1, T2>" <<endl;}// 局部特化第二个参数template <typename T1>class Data < T1, int >{public :Data();private :T1 _d1 ;int _d2 ;};template <typename T1>Data<T1 , int>:: Data(){cout<<"Data<T1, int>" <<endl;}

ps:下面的例子可以看出,偏特化并 不仅仅是指特殊部分参数 ,而是针对模板参数更进一步的条件限
制所设计出来的一个特化版本 。
// 局部特化两个参数为指针类型

template <typename T1, typename T2>class Data < T1*, T2 *>{public :Data();private :T1 _d1 ;T2 _d2 ;T1* _d3 ;T2* _d4 ;};template <typename T1, typename T2>Data<T1 *, T2*>:: Data(){cout<<"Data<T1*, T2*>" <<endl;}// 局部特化两个参数为引用template <typename T1, typename T2>class Data < T1&, T2 &>{public :Data(const T1& d1 , const T2 & d2);private :const T1 & _d1;const T2 & _d2;T1* _d3 ;T2* _d4 ;};template <typename T1, typename T2>Data<T1 &, T2&>:: Data(const T1& d1 , const T2 & d2): _d1( d1 ), _d2( d2 ){cout<<"Data<T1&, T2&>" <<endl;}void test2 (){Data<double , int> d1;Data<int , double> d2;Data<int *, int*> d3;Data<int &, int&> d4(1, 2);}

模板的全特化和偏特化都是在已定义的模板基础之上,不能单独存在。

模板的分离编译

解决办法:
1. 在模板头文件 xxx.h 里面显示实例化->模板类的定义后面添加
template class SeqList ; 一般不推荐这种方法,
一方面老编译器可能不支持,另一方面实例化依赖调用者。(不推荐)
2. 将声明和定义放到一个文件 “xxx.hpp” 里面, 推荐使用这种方法 。

模板总结
【优点】
模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生。
增强了代码的灵活性。
【缺点】
模板让代码变得凌乱复杂,不易维护,编译代码时间变长。
出现模板编译错误时,错误信息非常凌乱,不易定位错误。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 吃了狗肉和绿豆怎么办 做的衣柜没有门怎么办 蒸馒头熟了会瘪怎么办 3dmax贴图太大了怎么办 嘴皮边缘颜色深怎么办 嘴巴周围肤色暗沉怎么办 中奖彩票被洗了怎么办 牙龈下面长米粒肉疙瘩怎么办 书画印章盖反了怎么办 金龙鱼一个月不吃东西怎么办 罗汉鱼头撞扁了怎么办 房顶开槽埋线白色不一样怎么办 顶上灯挪位置线怎么办 马蜂窝弄掉又来怎么办 蜂窝弄掉又有怎么办 2018年小龙虾底板脏怎么办 一本分数线擦边过怎么办 玩具塑料球扁了怎么办 胶皮与海绵开了怎么办 安卓不支持flash了怎么办 看视频要加载flash怎么办 下水道管子铁皮破了怎么办 炸金花牌一样大怎么办 玩棋牌游戏输了怎么办 苹果7插耳机外放怎么办 出国种菠菜抓了怎么办 在菲做菠菜抓到怎么办 3串1中两个怎么办 微博账号封停怎么办 阴阳师账号被永久封停怎么办 寒刃2账号被禁用怎么办 输了好多钱我该怎么办 亲朋打鱼别处在玩怎么办 做糯米蛋的蛋清怎么办 水田地没耙地平怎么办 宝宝拉鸡蛋花样大便怎么办 电子琴伴奏区无旋律音怎么办 手机触摸屏摔坏了怎么办 手机充着电玩游戏卡怎么办? 4个月宝宝拉肚子怎么办 6个月宝宝上火怎么办