什么是模板

一、什么是模板?

模板是实现代码重用机制的一种工具,实质就是实现类型参数化,即把类型定义为参数。

两类模板,一是函数模板,二是类模板。

所谓函数模板,就是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。凡是函数体相同的函数都可以用这个模板来代替,不必定义多个函数,只需在模板中定义一次即可。在调用函数时系统会根据实参的类型来取代模板中的虚拟类型,从而实现了不同函数的功能。

 类模板,与函数模板类似,针对一组类,使得类中的某些数据成员、成员函数的参数、成员函数的返回值,能够取任意类型(包括系统预定义的和用户自定义的)。它的作用不是代表一个具体的、实际的类,而是代表着一组类家族的抽象。

二、模板的语法


1.函数模板: template<typename {类型参数名称},[其它形参]>函数返回类型 函数名称(函数参数)
{
     函数实现
}

template <typename T>T ReturnBiger(T& data1,T& data2)
{
    return ((data1 - data2)>0?data1 :data2);
}

2.类模板:template<typename{类型参数名称},[其它形参]>class 类模板名称
{
类实现
}

template<typename T> class Test
{
private:
    T n;
    const T i;
    static T cnt;
public:
    Test():i(0){  cout<<"Constructor implemented inside the class"<<"/T的类型为"<<typeid(T).name()<<"/输入值为空" << endl;}
    Test(T k);
    ~Test(){}
    void print();
    T operator+(T x);
};

类模板中的函数定义有两种模式,一是定义在类里面,二是在类外定义,后一种的语法为:template<typename{类型参数名称}> 函数返回类型 类名<参数名称>::函数名称(参数)
 template<typename T> Test<T>::Test(T k):i(k)
{
 cout<<"Constructor implemented outside the class"<<"/T的类型为"<<typeid(T).name()<<"/输入值为" << k<<endl;
}

模板类函数外部定义的格式很好记忆,可以跟普通的类函数对比记忆,普通的类函数定义如下:
函数返回类型 类名::函数名称(参数)
{
函数体
}

模板类的函数定义与普通类的函数定义类似,记住有两点区别就行了,第一是在最前面需要加上template<typename 类型参数名称>部分,其二是在类名字后面也跟着<>,里面存放形参,可以不跟参数类型(原因就是由于类不是一个确定的类,所以需要形参),除此两点之外,就跟普通的类函数定义完全一样了。

三、模板的使用

模板的使用,就是模板的特化。模板的使用者是编译器,编译器最终会将模板编译成一个个具体的函数或者具体的类,这个特化发生在编译期间,当编译器发现模板被应用时(不是定义)的时候。因此这就要求模板的实现必须在使用点之前,模板的实现只能在头文件中了(这是由于编译器不知道模板参数的具体类型,无法为其成员函数生成代码.编译器只用在成员函数的调用处,才最终知道如何生成代码。模板类的成员函数的定义也不能像普通类的成员函数一样定义在实现文件中,而是声明和实现都应该在头文件)。最终生成的代码中是不会有函数模板的,有的是通过函数模板生成的某个数据类型的函数。

对函数模板来说,有两种使用模式,显式类型参数和隐式类型参数模式
 
以ReturnBiger为例,

int n1=10;int n2=20;
ReturnBiger(n1,n2); //T被int代替

double d1=10;double d2=20;
ReturnBiger(d1,d2);//T被double代替
ReturnBiger(20.0,200.1); //T被float代替

上述为隐式实例化。下述为显式实例化
ReturnBiger<double>(d1,d2);// T被double代替
ReturnBiger<int>(n1,n2); //T被int代替

只用遇到上述语句的时候,编译器才会使用该函数模板,使用传给自己的数据类型作为实参生成一个实例函数。如果我们程序中只是定义了一个函数模板,而没有去使用它(隐式实例化、显试实例化)时,是不会有与该函数模板对应的函数定义的。

多说一句,ReturnBiger(d1,n1)会有什么结果?编译器报错!编译器按最先遇到的实参的类型隐含地生成一个模板函数,并用它对所有模板函数进行一致性检查,结果发现n1不是double型,因此报错。

对类模板来说,与函数模板不同的:函数模板的实例化可以由编译器在处理函数调用时自动完成的,而类模板的实例化必须由程序员在程序中显式地指定,只能使用显式模式。可以想象平时我们如何使用一个类。类是对象的抽象,对象是类的实例,则类模板是类的抽象,类是类模板的实例。即类模板->特定类->类对象,在类模板名之后在尖括号内指定实际的类型名,在进行编译时,编译系统就用确定的类型取代类模板中的类型参数T,这样就把类模板具体化了,或者说实例化了
模板类名字<具体的参数类型> 对象名字, 然后生成的对象就是一个普通的类对象,如下,其中a1到a4就是由类模板创建的4个普通的类对象

  Test<int> a1(10);
  Test<int> a2;
  Test<double> a3(10);
  Test<float> a4(10.7);
上述语句的执行结果如下:
Constructor implemented outside the class/T的类型为int/输入值为10
Constructor implemented inside the class/T的类型为int/输入值为空
Constructor implemented outside the class/T的类型为double/输入值为10
Constructor implemented outside the class/T的类型为float输入值为10.7

类模板也是给编译器使用的;只有在代码中隐式的或者显示的使用了模板具体化功能,编译器在编译程序时才会使用类模板所定义的“处方”来生产出一个特定类型的类。只是包含类模板而不去实例化使用它,是不会生产出类定义的。
同时编译器在需要对象之前,是不会生成类的隐式实例化的:
vector<double,20> *pt;  //是一个指针,而不需要一个对象,所以这里没有生成vector<double,20>的类定义
pt = new vector<double,20>;//现在需要一个该类的对象,所有这时编译器会使用vector模板生成vector<double,20>类 定义

四、其它问题
模板中的typename关键字与class关键字通用。
C++中,函数模板与同名的非模板函数重载时,应遵循下列调用原则:非模板函数最优先,显式具体化函数次之,模板函数最后适配。

模板功能应用的典型是通过一系列模板类形成的完整类库,特别是STL和ATL。标准C++库(STL)提供了很多可重用和灵活的类及算法,而ATL则是使用C++进行COM编程的事实标准。模板相当于特殊的宏,泛型技术就是写于类型无关的代码,而STL就是模板和泛型技术的结合了。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值