C++模板

要写一个通用函数,一般有以下几个方法:
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)因此而产生。
增强了代码的灵活性。
【缺点】
模板让代码变得凌乱复杂,不易维护,编译代码时间变长。
出现模板编译错误时,错误信息非常凌乱,不易定位错误。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值