c++(基础)————模板(函数模板与类模板)

一、模板是干什么的?

比如实现一个简单的比较大小的函数,如果要比较int、double、char等多种类型的数据,那么有多少中数据类型,我们就得实现多少个比较函数,然而这些函数除了类型不同之外,其他代码都是一样的(也就是代码实现的功能都相同,只是类型不同)。所以为了解决这种问题,我们就引入了模板,模板就是为了代码重用而生的

二、函数模板:

函数模板提供了一种函数行为,该函数行为可以用多种不同的类型进行调用;也就是说它和普通的函数很相似,唯一的区别就是函数的元素未确定(这些元素在使用时被参数化)。

1、定义函数模板:

template <typename T>		//T是模板的类型参数
T const& max(T const& a, T const& b)	//返回两者中较大的数
{
    return a > b ? a : b;
}

上面定义中,类型参数T表示的是,调用者调用这个函数时所指定的任意类型,可以使用任何类型来实例化该类型参数,只要所用类型提供模板使用的操作就可以。

2、使用函数模板:

#include <iostream>
#include <string>
using namespace std;

template <typename T>
T const& Max(T const& a, T const& b)
{
    return a > b ? a : b;
}

int main()
{
    int a1 = 3, b1 = 4;
    cout << "int: " << Max(a1, b1) << endl;	//用整型实例化参数

    char a2 = '1', b2 = '2';
    cout << "char: " << Max(a2, b2) << endl;	//用char实例化参数

    double a3 = 3.123, b3 = 3.124;
    cout << "double: " << Max(a3, b3) << endl;	//用double实例化参数

    string a4 = "asd", b4 = "asx";
    cout << "string: " << Max(a4, b4) << endl;	//用string实例化参数
    return 0;
}

输出:
int: 4
char: 2
double: 3.124
string: asx

对于上述例子而言,并不是把模板编译成一个可以处理任何类型的单一实体,而是对与实例化模板参数的每种类型,都从模板产生出一个不同的实体。即针对上边四种类型的每一种,都被Max()编译了一次。

1、模板实例化:
在这里插入图片描述

2、模板实参的推演(类型自推):模板参数的推演并不适合返回值类型。
在这里插入图片描述

3、函数模板参数:

1、不显示指定模板实参:

通常模板参数与调用参数是相关的,这个概念称为:函数模板的实参推演(类型自推),可以像普通函数那样调用函数模板。

template <typename T>
T const& Max(T const& a, T const& b)
{
    return a > b ? a : b;
}
...
cout << Max(4, 5) << endl;	//用int来实例化T

2、显式指定模板实参:

有些特殊情况,比如不能由调用参数来决定模板参数时,在调用时必须显示指定模板实参。

template <typename T1,typename T2,typename RT>
RT const& Max1(T1 const& a, T2 const& b);

模板参数的推演并不适合返回值类型,然而RT并没有出现在调用参数的类型里面,函数调用并不能推演出RT,因此,必须显示的指定模板实参列表。

template <typename T1,typename T2,typename RT>
RT const& Max1(T1 const& a, T2 const& b)
{
    return a > b ? a : b;
}
...
cout << Max1<int ,int,int>(4, 5) << endl;

3、只显示指定第一个模板实参:

这种情况仅适用于第一个模板参数作为函数返回值。

template <typename RT,typename T1, typename T2>
RT const& Max1(T1 const& a, T2 const& b)
{
    return a > b ? a : b;
}
...
cout << Max1<int>(4, 5) << endl;

4、重载函数模板:

和普通函数一样,函数模板也可以被重载。对于非模板函数和同名的函数模板,如果其他条件都相同,那么再调用的时候,重载解析过程通常会调用非模板函数。

值得注意的是,模板不允许自动类型转换,但普通函数可以进行自动类型转换。

template <typename T>
T const& Max(T const& a, T const& b)
{
    cout << "template:" << (a > b ? a : b);
    return a > b ? a : b;
}

int const& Max(int const& a, int const& b)
{
    cout<<"void:"<< (a > b ? a : b);
    return (a > b ? a : b);
}
int main()
{
    int a1 = 3, b1 = 4;
    Max(a1, b1) ;	//调用普通函数

    char a2 = '1', b2 = '2';
    Max(a2, b2) ;	//调用模板函数

    double a3 = 3.123, b3 = 3.124;
    Max(a3, b3);	//调用模板函数

    string a4 = "asd", b4 = "asx";
    Max(a4, b4);	//调用模板函数
	   
	Max('a', 4);//调用普通函数(自动类型转换)
    return 0;
}

输出:
void:4
template:2
template:3.124
template:asx
void:97
请按任意键继续. . .

三、类模板:

1、定义类模板:

类模板的定义和函数模板是相似的

用标准库模板vector实现类模板Stack<>

template <typename T>
class Stack
{
private:
    vector<T> elems;    //存储元素的容器

public:
    void push(T const& val)    //入栈
    {
        elems.push_back(val);
    }

    void pop()     //出栈
    {
        if (elems.empty())
        {
            throw - 1;
        }

        elems.pop_back();
    }

    T top()const  //返回栈顶元素
    {
        if (elems.empty())
        {
            throw - 1;
        }
            
        return elems.back();
        
    }

    bool empty()const  //判空
    {
        return elems.empty();
    }
};

通过上述代码可知,这个类的类型是Stack,其中T是模板参数,当在声明中需要使用该类的类型时,必须使用Stack。

如果定义模板的成员函数在类外,则必须指定该成员函数是一个函数模板,而且还需要用这个类模板的完整类型限定符。比如上述代码中的push函数在类外实现如下:

template <typename T>
void Stack<T>::push(T const& val)    //入栈
{
    elems.push_back(val);
}

2、使用类模板:

为了使用类模板对象,必须显示的指定模板实参。

int main()
{
    Stack<int> St;

    for (int i = 0; i < 5; i++)
    {      
        St.push(i);		//入栈
    }
   
    while (!St.empty())		//判空
    {
        cout << St.top() << endl;		//栈顶
        St.pop();		//出栈
    }

    return 0;
}

以上述代码为例,对于所有调用的成员函数,都会实例化出基于int类型的函数代码(只是被调用了的)。另外一方面,如果类模板中含有静态成员,那么用来实例化的每种类型,都会实例化这些静态成员

1、给定大小的用发:

int main()
{	    	
    Stack<int> St[10];	//存除十个整形数据的栈

    return 0;
}

2、重命名:

typedef Stack<int>  int_Satck;	//Stack<int> 与 int_Satck 等价;

Stack<int> St1;	//ok
int_Satck St2;	//ok

3、模板套用:

Stack< Stack<int> > St;

3、类模板的特化:

1、全特化:

1、为了特化一个类模板,首先在起始处声明一个template<>,然后声明名来特化类的类型。这个类型被用作模板实参,且必须在类名的后面直接指定:

template <>
class Stack<string>
{
	...
}

2、进行类模板的特化时,每个成员函数都必须重新定义为普通函数,原来模板函数中的每一个T也相应地被进行特化的类型取代:

void Stack<string>::push(string const& val)    //入栈
{
    cout << "void Stack<string>::push(string const& val)" << endl;	//测试如果用string来实例化对象,看调用的是不是特化后的类模板方法。
    elems.push_back(val);
}

测试:
Stack<string> S;
S.push("1");

输出:
void Stack<string>::push(string const& val)
请按任意键继续. . .

2、局部特化:在特定的环境下指定类模板的特定实现,并要求某些模板参数必须由用户来定义

//类模板
template <typename T1,typename T2>
class MyClass
{
    ...
};
//局部特化1:两个模板参数类型相同
template <typename T>
class MyClass<T,T>
{
	...
};

//局部特化2:第二个模板参数类型为int
template <typename T>
class MyClass<T,int>
{
	...
};
//局部特化3:两个模板参数都是指针类型
template <typename T1, typename T2>
class MyClass<*T1, *T2>
{
	...
};

想要了解更多有关模板的知识,详见 《C++ Template The Complete Guide》。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值