模板

常见的代码重用技术有:
函数,类与对象,继承与派生,多态(函数重载,运算符重载,虚函数,纯虚函数,抽象类等),泛型程序设计

通用的代码需要不受数据类型的影响,并且可以自动适应数据类型的变化,这种程序设计类型称为泛型程序设计。
在c++中,泛型实际是通过参数多态性来实现的,参数化的多态是将程序所处理的对象的类型进行参数化,使同一段程序可以处理不同类型的对象。

值(Value)和类型(Type)是数据的两个主要特征,它们在c++中都可以被参数化

问题:定义一个函数,求两个变量之间的较大值,并且使所有基本数据类型都可以使用这个功能

  • 如果采用函数重载的方法,则需要的函数数量较多,并且除了操作的数据类型不同以外,程序框架是一样的,这使代码显得累赘并且加大维护难度
  • 如果采用宏定义的方法,虽然解决了代码维护的问题,但缺少类型检查,极大增加了出错的可能

此时,c的模板可以解决这一问题,数据类型本身就是一个参数。模板的声明或定义只能在全局,命名空间或类的内部进行,即不能在局部范围,函数内进行,如不能main()内声明或定义一个模板。
c
模板非常重要,整个标准库几乎都是使用模板来开发的,STL更是经典之作。

STL(Standard Template Library,标准模板库)就是c++对数据结构进行封装后的称呼

函数模板

函数模板的基本格式:

template <typename 形参名1,typename 形参名2…>返回类型 函数名(参数列表){函数体}

其中typename关键字也可以用class替换

  • 注意虽然在c中struct和class基本上是同等的,但是两者很重要的区别是c没有支持使用struct定义模板参数的方法

在函数内部此时既可以指定数据类型,也可以不指定而是用形参来代替,调用时根据实参的数据类型来确定相应变量的数据类型,如:

template <typename T1,typename T2>T1 max(T1 x,T2 y){return x>=y?x:y;}
int main(){
cout<<max(321.32,255)<<’ ';
cout<<max(32,‘2’)<<endl;
} //输出结果:321.32 50

此例中的max函数代表一类具有相同程序逻辑的函数,称之为函数模板。函数模板本身是无法编译的,因此不能直接使用,必须进行实例化。函数模板属于静态绑定,需要赋予具体的数据类型,编译器才能将模板扩展成合适的代码进行绑定。
由函数模板实例化出的函数称为模板函数,二者间的关系类似于类和对象,即函数模板将具有相同类型正文的一类函数进行抽象,对其实例化可得模板函数。
函数模板实例化的参数类型可以是类类型,但是使用时需注意类类型相对于基本数据类型有许多运算是无法进行的,在使用之前应对函数模板中涉及到该类的运算符进行重载,并确认对类对象的使用是否合法。
注意,同一个模板参数只能对应一种数据类型,如果输入的实参数据类型无法匹配,则编译时会产生混乱无法生成准确的函数,即使两种数据之间是可以转换的,如:

template<typename T>max(T x,T y);
int main(){
cout<<max(321.32,255);
}

此时编译器不知道应该生成max(int,int)还是max(float,float),即无法进行实例化,这很好理解因为编译器需要靠类型信息来进行绑定,如果类型无法匹配显然产生错误(如果类型转换也算的话,那么任何类型都一样可以转换,仍然无法匹配)。这种情况可以考虑使用多个模板参数或函数重载函数模板

使用多个模板参数,如:

template<typename T1,typename T2>max(T1 x,T2 y);

函数重载函数模板,如:

templatemax(T x,T y);int max(int x,int y);

当存在重载时,函数调用的依据还是最佳匹配原则:

  • 如果存在完全匹配时,则优先调用完全匹配,并且普通函数优于模板函数
  • 如果不能完全匹配,先看是否有提升转换(低类型->高类型),如:

char,short转换为int,float转换为double等,这种情况数据不会丢失

  • 无法进行提升转换,再看是否有标准转换(高类型->低类型),如:

int转化为char,long double转换为double等

  • 最后查看是否有用户自定义的转换,如类声明中定义的类型转换运算符

类模板

与函数一样,类也可以有模板
类模板的基本格式为:

template<typename 形参名1,typename 形参名2…>class 类名{};

同理,typename关键字可换成class,以及同样的可以在类模板内部使用模板形参对内置数据类型进行声明
在类模板外部定义成员函数的基本格式为:

template<模板形参列表>函数返回类型 类名<模板形参名>::函数名(参数列表){函数体}

使用类模板生成对象,同样需要对模板参数进行实例化,基本格式为:

类名<数据类型名> 对象名

例:双向链表类模板

template<typename T>class node
{
    T data;
    node *next,*prev;
public:
    node(){prev=NULL,next=NULL;}
    void setvalue(T data){this->data=data;}
    void append(node *p);
};
template<typename T>void node<T>::append(node *p)      //node<T>是模板的名字
{
    this->next=p->next;
    p->prev=this;
    if(next)next->prev=p;
    this->next=p;
}
int main()
{
    node<int>*nodehead,node,node1,node2;              //用模板实参int实例化类模板
    node1.setvalue(1),node2.setvalue(2);
    nodehead=&node;
    nodehead->append(&node1),nodehead->append(&node2);
}