C++基础——模板讲解

目录

一. 泛型编程

二. 函数模板

1.格式:

2.定义:

1.隐式实例化

2.显式实例化 

 3.解决方法3:使用多个T类型

4.在C++中编译器允许非模板函数和模板函数同时存在


一. 泛型编程

        先来看一段代码:

void Swap(int& p1, int& p2) {
	int tmp = p1;
	p1 = p2;
	p2 = tmp;
}

int main(){
    int a = 1, b = 2;
	cout << "a:" << a << "  b:" << b << endl;
	Swap(a, b);
	cout << "a:" << a <<"  b:"<

C++基础——模板讲解_第1张图片

        若是我们需要对浮点型数据进行交换函数,还得再去写一个Swap函数 :

void Swap(double& p1, double& p2) {
	double tmp = p1;
	p1 = p2;
	p2 = tmp;
}

void Swap(char& p1, char& p2) {
	char tmp = p1;
	p1 = p2;
	p2 = tmp;
}

int main() {
	int a = 1, b = 2;
	cout << "a:" << a << "  b:" << b << endl;
	Swap(a, b);
	cout << "a:" << a <<"  b:"<

C++基础——模板讲解_第2张图片          这就说明了一个问题:若有多个变量之间的数据交换,就要频繁去写各种类型的函数,函数体大致是一样的,只是参数类型不同,导致其中含有大量重复代码,代码耦合度高,更容易出bug。

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

二. 函数模板

1.格式:


template
返回值类型 函数名(参数列表){}

        class用于定义类,在c++引入模板后,最初定义模板的方法为:template,这里class关键字表明T是一个广泛的类型。后来为了避免class在这两个地方的使用可能给人带来混淆,所以引入了typename这个关键字。它的作用同class一样表明后面的符号为一个类型。这样在定义模板的时候就能够使用以下的方式了: template.在模板定义语法中关键字class与typename的作用全然一样。 T就是泛型——广泛类型,它里面包含内置类型和你所创建的自定义类型。

2.定义:


        一个类模板(也称为类属类或类生成类)同意用户为类定义一种模式。使得类中的某些数据成员、默写成员函数的參数、某些成员函数的返回值,能够取随意类型(包含系统提前定义的和用户自己定义的)。
        假设一个类中数据成员的数据类型不能确定。或者是某个成员函数的參数或返回值的类型不能确定。就必须将此类声明为模板,它的存在不是代表一个详细的、实际的类,而是代表着      (一类)类。

例:        

template 
void Swap(T& left, T& right) {
	T tmp = left;
	left = right;
	right = tmp;
}
int main() {
	int a = 10, b = 20;
	Swap(a, b);
	cout << "a:" << a << "  b:" << b << endl;
	double c = 33.3, d = 66.6;
	Swap(c, d);
	cout << "c:" << c << "  d:" << d << endl;
	char e = 'y', f = 'r';
	Swap(e, f);
	cout << "e:" << e << "  f:" << f << endl;
	return 0;
}

C++基础——模板讲解_第3张图片

        编译器在指向模板函数的调用时,会先进行类型推演,它会把接收到的实参类型推演生成对应类型的函数以供调用。

C++基础——模板讲解_第4张图片

        例如:当double类型数据被作为实参去调用函数模板时,编译器通过实参的double类型进行推演,将T确定为double类型,然后专门产生一份处理double类型的代码。

        其次需要注意的是:使用函数模板时,其函数会实例化出对象,在上面的代码中,共有三种类型数据进行三次函数调用,那么就会实例化出三个对象。但若是同种类型的多次调用,编译器只会生成一个对象:

int a = 10, b = 20;
	Swap(a, b);
	cout << "a:" << a << "  b:" << b << endl;
int x = 500, y = 1220;
	Swap(x, y);
	cout << "a:" << a << "  b:" << b << endl;
int r=63,t=79;
    Swap(r, t);
	cout << "r:" << r << "  t:" << t << endl;

        如上图,这也是进行了三次模板函数的调用,但只实例化出一个对象,这是因为模板函数对于同种类型的多次调用,也只会实例化出一个对象。

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

1.隐式实例化

        看代码:

template
T Add(const T& l, const T& r) {
	return l + r;
}

int main() {
	int a1= 1,a2=2;
	double b = 5.6,b2=7.8;
	Add(a1,a1);
    Add(b1,b2);
    //两种不同类型的数据相加
    Add(a1,b1);
	return 0;
}

        前两个Add函数是两个同类型间相加,第三个是int和double类型相加,这不会通过编译,在编译期间编译器看到模板函数调用时,会实例化出对象,需要推演其实参的类型,通过参数一将T推演成int,通过参数二将T推演成double,但模板只有一个T,无法确定T是int还是double,因此报错。注:编译器可不会因为两种类型的不同而进行类型转换操作!!! 

所以解决方法1:就是在编译器推演实参类型前,就把两个不同类型的转换为同一类型

    Add(a, (int)c);
	Add((double)a,c);

        这种方式称为隐式实例化,第一个Add函数先将变量c转换为int类型,这样就可以进行推演了,T最终会变成int类型;第二个Add函数 将变量a转换为double类型,T最终变成double型。

C++基础——模板讲解_第5张图片

2.显式实例化 

        在函数名后加一对尖括号,里面填写你最终要转换出的类型,这也是T的最终类型。

int main() {
	int a = 10;
	double b = 20.3;
	Add(a, b);
	cout << "Add:" <(a,b) << endl;
	Add(a, b);
	cout << "Add:" << Add(a, b) << endl;
	return 0;
}

C++基础——模板讲解_第6张图片

 3.解决方法3:使用多个T类型

template 
T1 Add2(const T1& left, const T2& right) {
	return left + right;
}

int main() {
	int a1 = 10, a2 = 20;
	double b1 = 10.1, b2 = 20.2;
	cout << Add2(a1, a2) << endl;
	cout << Add2(b1, b2) << endl;
	cout << Add2(a1, b2) << endl;
	cout << Add2(b1, a2) << endl;	//最终结果的类型是根据第一个参数类型所决定
	return 0;
}

        直接写出两个T类型,这样即使不用隐式和显式转换也可以进行不同类型间的数据相加,最终结果的类型可以设置为int,也可以是double。

C++基础——模板讲解_第7张图片


4.在C++中编译器允许非模板函数和模板函数同时存在

例:

template 
T1 Add( T1& left,T2& right) {
	return left + right;
}

int Add(int& left, int& right) {
	return left + right;
}
int main() {
	int a = 15, b = 30;
	double c = 33.3, d = 99.9;
	
	Add(a, b);	
	Add(c, d);

	return 0;
}

结果: 

C++基础——模板讲解_第8张图片

        对于非模板函数和同名函数模板,如果其他条件都相同,编译器会优先调用非模板函数,而模板函数可以生成更加匹配的版本。所以第一个Add函数内部的数据都是int,那么编译器肯定会选择现有的int Add函数去调用,其实编译器也很懒的,哈哈哈~。

C++基础——模板讲解_第9张图片

你可能感兴趣的:(C++,c++)