C++模板到底是个啥,看这就透了
Posted 两片空白
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++模板到底是个啥,看这就透了相关的知识,希望对你有一定的参考价值。
目录
前言
我们知道C++相对于C语言新增了一个功能函数重载,简单来说函数重载是对于类型不同的参数,可以存在相同的函数名,编译器会通过实参类型来调用相应的函数。
但是,这样会有几个不大实用的地方。
- 重载函数只是参数类型不同,代码的复用率比较低,只要由新的类型出现,就需要增加对应的函数。
- 代码的可维护性比较低,一个出错可能所有重载都会出错。
为了很好的解决上面的问题,于是C++增加了一个新的功能,叫模板。
一.定义
想要了解模板,首先我们来了解一下泛型编程。
泛型编程:编写与类型无关的通用代码,是代码复用的一种手段,是针对广泛类型,与类型无关。模板是泛型编程的基础。
在编写代码时,一些函数和类,针对不同的类型需要些很多重复的代码。
函数比如我们要编写一个交换函数swap,当要交换int,double,char类型时,我们就需要编写三个重载函数。
类,比如我们想实现数据结构栈stack时,比如定义多个对象st1和st2,st1数据要存储int类型,而st2要存储double类型时,我们就需要写两个栈。
模板就是针对类型不同而逻辑相同的函数或类,编写一个模板(具体如何编写,下面由有代码),于是我们用户就不用因为类型不同,要编写多个函数或者类。编译器会通过模板生成对应的函数或类。
因此模板分为两类
二.函数模板
2.1 概念
函数模板就好像代表了一个函数的家族。该函数模板与类型无关,在使用时被参数化,根据实参类型产生对应类型的函数。
2.2 函数模板格式
函数模板使用到关键字template
例如写一个交换函数:
template<class T>
void Swap(T& x, T& y){
T temp = x;
x = y;
y = temp;
}
注意:
- 模板并不是函数,函数调用时,调用的也不是模板,而是模板对应生成的函数。并且类型不同参数调用时,调用的也不是同一个函数。
- typename时定义模板参数的关键字,也可以使用class,但是不能使用struct。
2.3 函数调用的原理
函数模板本身并不是一个函数,是编译器通过模板来产生特定类型的具体函数。也就是本来是有我们来编写不同类型参数的函数的,现在我们通过模板将其交给了编译器来做。
原理是:我们写了一个模板,编译器通过模板实例化出对应函数和类。
用交换函数举例:
在编译器编译阶段,对于函数模板的使用,编译器要通过传入实参类型来生成对应类型的函数来进行调用。这也说明在预处理阶段编译器就会生成对应的函数。简单来说,模板就是将T确定为对应传入参数的类型,T修饰的变量或对象就是传入参数的类型。
2.4 函数模板的实例化
用不同类型的参数使用模板,编译器推演得到对应函数叫做函数模板的实例化。
实例化分为:隐式实例化和显示实例化
- 隐式实例化:让编译器根据实参推演模板参数的实际类型。T的类型是编译器推到的。
#include<iostream>
using namespace std;
template<class T>
T Add(const T& x, const T& y){
return x + y;
}
int main(){
int a = 2, b = 9;
//没有明确给出参数类型
Add(a, b);
double x = 1.1, y = 2.2;
Add(x, y);
return 0;
}
- 显示实例化:在函数名后的<>中指定模板参数的实际类型。T就是<>里的类型。
#include<iostream> using namespace std; template<class T> T Add(const T& x, const T& y){ return x + y; } int main(){ int a = 2, b = 9; //参数显示给出 Add<int>(a, b); double x = 1.1, y = 2.2; Add<double>(x, y); return 0; }
#include<iostream>
using namespace std;
//通用加法函数
template<class T1,class T2>
T1 Add(const T1& x, const T2& y){
return x + y;
}
int main(){
Add<int,double>(a, x);
return 0;
}
注意在隐式实例化的情况下,如果有多个实参但是类型不同,但是模板中只有一个参数T,此时编译会报错。因为编译器无法确定T为什么类型。
#include<iostream>
using namespace std;
template<class T>
T Add(const T& x, const T& y){
return x + y;
}
int main(){
int a = 2;
double x = 1.1;
//a和x参数不同,模板中只有一个参数T
//编译报错
Add(a, x);
//解决:1.强转类型转化,2.显示实例化
Add(a,(int)x);
Add<int>(a,x);
return 0;
}
2.5 函数模板的匹配规则
- 一个非模板函数可以和一个同函数名的模板同时存在,并且该函数模板还可以实例化为这个非模板函数。
#include<iostream>
using namespace std;
//专门处理int类型加法函数
int Add(const int& x, const int& y){
return x + y;
}
//通用加法函数
template<class T>
T Add(const T& x, const T& y){
return x + y;
}
int main(){
Add(1, 2);//调用非模板函数
//<int>调用函数不这样写,这样写就是调用模板
Add<int>(1, 2);//调用编译器通过模板推演出来的函数
return 0;
}
- 对于非模板函数和同名函数模板,如果其它条件相同,在调用时会优先调用非模板函数,而不会从该模板产生出一个实例。如果模板可以产生一个更好匹配的函数,那么将选择该模板。
#include<iostream>
using namespace std;
//专门处理int类型加法函数
int Add(const int& x, const int& y){
return x + y;
}
//通用加法函数
template<class T1,class T2>
T1 Add(const T1& x, const T2& y){
return x + y;
}
int main(){
//与非模板函数完全匹配,不需要编译器通过模板推演出函数
Add(1, 2);
int a = 2;
double x = 1.1;
//非模板函数不适用,调用模板函数
Add(a, x);
return 0;
}
- 模板函数不允许自动类型转化,当普通函数可以自动类型转化。
三.类模板
3.1 类模板格式
定义一个学生类:
template<class T1,class T2,class T3>
class Student{
public:
//构造函数
Student(T1 name,T2 age,T3 score){
_name=name;
_age=age;
_score=score;
}
private:
T1 _name;
T2 _age;
T3 _score;
}
3.2 类模板的实例化
类的实例化与函数实例化不同,类实例化必须显示实例化。即类实例化时必须在类名后面跟<>,然后将类型放在<>里。
类模板并不是真正的类,实例化出来的才是真正的类。
//student是类名,student<string,int,float>才是类型
Student<string,int,float>s("Tom",18,85.5);
student是类名,student<string,int,float>才是类型
3.3 类的函数模板
以上是关于C++模板到底是个啥,看这就透了的主要内容,如果未能解决你的问题,请参考以下文章