【C++】一文教你认识模板
创始人
2025-05-28 06:26:13

目录

  • 1.泛型编程
  • 2.函数模板
    • 2.1函数模板的原理
    • 2.2函数模板的实例化
    • 2.3模板参数的匹配原则
  • 3.类模板
    • 3.1类模板的定义格式
    • 3.2类模板的实例化

1.泛型编程

我们先来看一个swap函数

void Swap(int& a,int& b)
{int c = a;a = b;b = c;
}void Swap(double& a, double& b)
{double c = a;a = b;b = c;
}void Swap(char& a, char& b)
{char c = a;a = b;b = c;
}

像这么一个简单的Swap函数,因为参数的类型不同,我们就要针对不同的类型来创建多个Swap函数,这样的情况虽然是以函数重载可以实现,但有以下两点不好的地方:

  1. 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数
  2. 代码的可维护性比较低,一个出错,可能所有的重载均出错。

那我们能不能告诉编译器一个模子,让编译器根据不同的类型利用该模子来生成代码 呢?

古人似乎也面对过这样的问题,一个人写出了一篇照耀古今的文章,想要将其传播出去,但又受限于科技的发展,只能照人手抄,这就限制了知识的传播,直到活字印刷的出现,这一现象发生革命性的改变

在这里插入图片描述

以框为模板,只要我们替换里面的模块,就能印刷出不同的文章,不得不佩服古人的智慧。

如果C++中,也能存在这样一个模具,那将会节省我们很多时间,推迟掉发的速度。刚好C++中就有这样的方法。

泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。

在C++中,模板分为函数模板类模板

2.函数模板

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

函数模板格式如下:

template

返回值类型 函数模板名(T1 参数名, …, Tn 参数名){}

注意:

  1. typename是用来定义模板参数的关键字,也可以使用class,但不能用struct。
  2. template设定模板参数与下面紧挨着的函数统称为函数模板,对应的模板参数不能在其他函数内使用,想要用只能继续创建函数模板。
  3. 模板参数设置几个就要用几个,不能有剩余的,编译器会报错,如下:
template
T Add(const T& a, const T& b)
{return a + b;
}int main()
{int a1 = 1,a2 = 2;Add(a1,a2);return 0;
}

在这里插入图片描述

template
void Swap(T& a,T& b)
{T c = a;a = b;b = c;
}int main()
{int a = 1;int b = 2;Swap(a, b);cout << a << " " << b << endl;return 0;
}

在这里插入图片描述

2.1函数模板的原理

函数模板是一个模板,不是函数,是编译器用使用方式产生特定具体类型函数的模具。所以其实模板就是将本来应该我们做的重复的事情交给了编译器。

我们编写如下代码查看调用函数模板是否地址相同(在汇编环境下查看,若是直接调试,调用的都是函数模板):

template
void Swap(T& a,T& b)
{T c = a;a = b;b = c;
}int main()
{int a = 1, b = 2;Swap(a, b);double c = 1.0, d = 2.0;Swap(c, d);return 0;
}

在这里插入图片描述

如上图,两次调用的函数的地址不同,

而如果是下面相同类型的两次函数的调用,则地址相同:

template
void Swap(T& a,T& b)
{T c = a;a = b;b = c;
}int main()
{int a = 1, b = 2;Swap(a, b);int c = 3, d = 4;Swap(c, d);return 0;
}

在这里插入图片描述

结论: 在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此,相同的类型调用相同的函数

在这里插入图片描述

拓展:

Swap函数是经常使用的函数,在库内也就定义好了,我们想要使用可以直接调用库内的swap函数即可

在这里插入图片描述

#include
using namespace std;int main()
{int a = 1, b = 2;swap(a, b);cout << a << " " << b << endl;return 0;
}

在这里插入图片描述

2.2函数模板的实例化

用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化有分为:隐式实例化和显示实例化。

  1. 隐式实例化:让编译器根据实参推演模板参数的实际类型。

    template
    T Add(const T& a, const T& b)
    {return a + b;
    }int main()
    {int a1 = 1, a2 = 2;cout << Add(a1, a2) << endl;double b1 = 1.1, b2 = 2.2;cout << Add(b1, b2) << endl;return 0;
    }
    

    在这里插入图片描述

    若是执行下面的语句,则会报错

    	Add(a1, b1);
    

    因为在编译期间,当编译器看到该实例化时,需要推演其实参类型,通过实参a1将T推演为int,通过实参b1将T推演为double类型,但模板参数列表中只有一个T,编译器无法确定T到底是什么类型,因此报错。

    注意: 在模板中编译器不会进行自动类型转换,因为编译器毕竟是编译器它没有那么智能,不会揣摩程序员的心思,知道是转int还是double所以直接不转。

    想要解决这个问题有三种方法:

    1. 用户自己强制类型转换

      	Add(a1, (int)b1);//结果为int类型的值Add((double)a1, b1);//结果为double类型的值
      
    2. 在创造一个模板参数,使用两个模板参数接收两个参数

      template
      T1 Add(const T1& a, const T2& b)
      {return a + b;
      }int main()
      {int a1 = 1;double b1 = 1.1;cout << Add(a1, b1) << endl;return 0;
      }
      

      缺陷:但需要返回的是什么类型仍需要程序员自己挑动。

    3. 使用显示实例化

      	Add(a1,b1);//将参数全部置为int类型Add(a1,b1);//将参数全部置为double类型
      
  2. 显式实例化:在函数名后的<>中指定模板参数的实际类型。

    int main()
    {int a1 = 1;double b1 = 1.1;cout << Add(a1, b1) << endl;cout << Add(a1, b1) << endl;return 0;
    }
    

    在这里插入图片描述

    如果类型不匹配,编译器会尝试按照<>内的类型进行隐式类型转换,如果无法转换成功,编译器会报错。

2.3模板参数的匹配原则

  1. 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数

    template
    T Add(const T& a)
    {return a + b;
    }int Add(const int& a, const int& b)
    {return a + b;
    }int main()
    {Add(1, 2);Add(1, 2);return 0;
    }
    

    如上面的Add(1,2),调用的是非模板函数,因为传到就是int型的参数,调用非模板函数直接就可以使用,而调用模板函数还需要实例化,编译器会选择调用更省时省力的函数。

    Add(1,2),因为显示实例化的存在只会去调用函数模板,将其实例化为与非模板函数相同的函数之后执行函数。

  2. 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生一个实例,如果模板可以产生一个具有更好匹配的函数,那么将选择模板。

    template
    T1 Add(const T1& a, const T2& b)
    {return a + b;
    }int Add(const int& a, const int& b)
    {return a + b;
    }int main()
    {Add(1, 2);//与非模板函数匹配,编译器不需要特化Add(1, 2.0);//调用函数模板return 0;
    }
    

    Add(1,2)与非模板函数完全吻合,调用非模板函数,省时省力。

    Add(1,2.0)类型不同,只能通过模板实例化出相同类型的函数,调用模板。

  3. 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换。

    int Add(const int& a, const int& b)
    {return a + b;
    }int main()
    {cout << Add(1, 2.0) << endl;return 0;
    }
    

    在这里插入图片描述

3.类模板

3.1类模板的定义格式

template
class 类模板名
{//类内成员定义
};

使用类模板编写如下代码

template
class Stack
{
public:Stack(int capacity = 4){_a = new T[capacity];_top = 0;_capacity = capacity;}//声明和定义分离~Stack();private:T* _a;int _capacity;int _top;
};//在类外定义
template
Stack::~Stack();
{delete[] _a;_top = _capacity = 0;
}

注意:

  1. 类模板内的函数在类内声明,类外定义的写法要注意,与正常类不同。
  2. 类模板如果声明和定义分离,需要在一个文件内完成,不能拆分为两个文件,比如:声明放在.h文件,定义放在.c文件这是不允许的。

3.2类模板的实例化

类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板的名字不是真正的类实例化的结构才是真正的类。

使用上面编写的类模板

template
class Stack
{
public:Stack(int capacity = 4){_a = new T[capacity];_top = 0;_capacity = capacity;}~Stack(){delete[] _a;_top = _capacity = 0;}
private:T* _a;int _capacity;int _top;
};int main()
{Stack st1;Stack st2;return 0;
}
  • Stack不是类名,不是真正的类,Stack<类型>才是类

相关内容

热门资讯

联动云租一天多少钱(联动云租一... 本篇文章极速百科给大家谈谈联动云租一天多少钱,以及联动云租一天怎么划算对应的知识点,希望对各位有所帮...
飞机托运收费(飞机托运收费多少... 本篇文章极速百科给大家谈谈飞机托运收费,以及飞机托运收费多少钱一公斤对应的知识点,希望对各位有所帮助...
挡泥板(挡泥板是什么意思) 挡... 本篇文章极速百科给大家谈谈挡泥板,以及挡泥板是什么意思对应的知识点,希望对各位有所帮助,不要忘了收藏...
滴滴专车官网(滴滴专车司机网站... 今天给各位分享滴滴专车官网的知识,其中也会对滴滴专车司机网站进行解释,如果能碰巧解决你现在面临的问题...
路特斯跑车(路特斯跑车多少钱一... 今天给各位分享路特斯跑车的知识,其中也会对路特斯跑车多少钱一辆2023款进行解释,如果能碰巧解决你现...
丰田致享新车报价(丰田致享20... 今天给各位分享丰田致享新车报价的知识,其中也会对丰田致享2021款报价进行解释,如果能碰巧解决你现在...
聊城到潍坊的汽车(聊城到潍坊的... 本篇文章极速百科给大家谈谈聊城到潍坊的汽车,以及聊城到潍坊的汽车票价多少对应的知识点,希望对各位有所...
没有身份证怎么买票(没有身份证... 今天给各位分享没有身份证怎么买票的知识,其中也会对没有身份证怎么买票进行解释,如果能碰巧解决你现在面...
2018科目三灯光详细表(20... 本篇文章极速百科给大家谈谈2018科目三灯光详细表,以及2018科目三最新模拟灯光考试20组不重复完...
五菱之光v(五菱之光v和五菱之... 今天给各位分享五菱之光v的知识,其中也会对五菱之光v和五菱之光有什么区别进行解释,如果能碰巧解决你现...
摩托车怠速(摩托车怠速多少转正... 今天给各位分享摩托车怠速的知识,其中也会对摩托车怠速多少转正常进行解释,如果能碰巧解决你现在面临的问...
武汉到西安(武汉到西安火车时刻... 今天给各位分享武汉到西安的知识,其中也会对武汉到西安火车时刻表查询进行解释,如果能碰巧解决你现在面临...
五菱之光v图片(五菱之光v新车... 今天给各位分享五菱之光v图片的知识,其中也会对五菱之光v新车报价进行解释,如果能碰巧解决你现在面临的...
郑州到重庆火车(郑州到重庆火车... 本篇文章极速百科给大家谈谈郑州到重庆火车,以及郑州到重庆火车多少钱一张对应的知识点,希望对各位有所帮...
学生证优惠区间(学生证优惠区间... 今天给各位分享学生证优惠区间的知识,其中也会对学生证优惠区间没有盖章进行解释,如果能碰巧解决你现在面...
武汉到合肥(武汉到合肥多少公里... 今天给各位分享武汉到合肥的知识,其中也会对武汉到合肥多少公里进行解释,如果能碰巧解决你现在面临的问题...
软座座位分布图(k8412软座... 本篇文章极速百科给大家谈谈软座座位分布图,以及k8412软座座位分布图对应的知识点,希望对各位有所帮...
长安逸动dt(长安逸动dt空调... 本篇文章极速百科给大家谈谈长安逸动dt,以及长安逸动dt空调滤芯拆卸教程对应的知识点,希望对各位有所...
西安到达州(西安到达州火车时刻... 本篇文章极速百科给大家谈谈西安到达州,以及西安到达州火车时刻表查询对应的知识点,希望对各位有所帮助,...
野马蝰蛇(野马蝰蛇gt500图... 本篇文章极速百科给大家谈谈野马蝰蛇,以及野马蝰蛇gt500图片对应的知识点,希望对各位有所帮助,不要...
高速obu是什么意思(收费站o... 今天给各位分享高速obu是什么意思的知识,其中也会对收费站obu是什么意思进行解释,如果能碰巧解决你...
西安北站在哪(西安北站在哪进站... 今天给各位分享西安北站在哪的知识,其中也会对西安北站在哪进站进行解释,如果能碰巧解决你现在面临的问题...
汽车搭电一次多少钱(汽车搭电大... 今天给各位分享汽车搭电一次多少钱的知识,其中也会对汽车搭电大概多少钱进行解释,如果能碰巧解决你现在面...
宝马跑车敞篷价格(宝马跑车敞篷... 本篇文章极速百科给大家谈谈宝马跑车敞篷价格,以及宝马跑车敞篷价格图片对应的知识点,希望对各位有所帮助...
cbr650r(cbr650r... 本篇文章极速百科给大家谈谈cbr650r,以及cbr650r座高对应的知识点,希望对各位有所帮助,不...
在哪买机票最便宜(在哪买机票最... 今天给各位分享在哪买机票最便宜的知识,其中也会对在哪买机票最便宜票进行解释,如果能碰巧解决你现在面临...
etc办理点(etc办理点节假... 今天给各位分享etc办理点的知识,其中也会对etc办理点节假日休息吗进行解释,如果能碰巧解决你现在面...
宝马1181报价及图片(宝马1... 今天给各位分享宝马1181报价及图片的知识,其中也会对宝马1181报价及图片及价格进行解释,如果能碰...
限行处罚扣分吗(限行被扣分吗)... 本篇文章极速百科给大家谈谈限行处罚扣分吗,以及限行被扣分吗对应的知识点,希望对各位有所帮助,不要忘了...
车车(车车车念什么) 车车 车... 今天给各位分享车车的知识,其中也会对车车车念什么进行解释,如果能碰巧解决你现在面临的问题,别忘了关注...