欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【C++基础】C++模板总结

程序员文章站 2022-06-01 11:55:22
...

一、模板

模板是泛型编程的基础,分为模板函数和模板类

泛型编程就是与类型无关的逻辑代码,是一种复用的方式。

那么为什么要有泛型编程呢?

C++是一门强类型的语言,无法像动态语言那样,可以写一段逻辑代码,把任意类型传进去。为了解决这个问题所以有了泛型编程,通过把通用的逻辑设计为模板,可以摆脱类型的限制,提供了继承机制以外的又一种抽象机制,提升了代码的复用性

注意:模板不参与编译,是编译器根据用户使用时提供的类型参数生成代码,这个过程叫做模板的实例化,模板又分为模板函数和模板类。

二、模板函数

模板可以使我们生成通用的函数,这个函数可以接受任意类型的参数,也可以返回任意类型的值,不需要对所有可能的类型进行函数重载,这在一定程度实现了宏的作用。

函数模板的格式:

template<class 参数名1, clss参数名2, ... , clss 参数名n>
返回值 函数名(参数列表)
{代码}

模板中形参的定义可以使用class,也可以使用typename,含义是相同的。

如下代码:

template<typename T>
bool IsEqual(const T& left, const T& right ) {
	return left == right;
}
void test() {
    string s1("s1"), s2("s2");
    cout << IsEqual(s1, s2) << endl;
    cout << IsEqual(1, 1) << endl;
    s2 = "s1";
    cout << IsEqual(s1, s2) << endl;
}

实现了一个模板IsEqual函数,用来比较两个T类型的数据是否相等,输出结果是0 1 1

三次调用IsEqual函数,每次参数的类型都不同,但是却都可以比较,这就是模板的作用,一份代码可以适用任意类型

1.模板函数的实例化

编译器调用模板函数时,编译器会根据函数传递的参数自动推演出模板参数的类型,并自动生成对应的代码

【C++基础】C++模板总结

如图,编译器调用IsEqual(s1, s2)时就会自动推演出一份形参为string的代码,下面也是同样的道理

汇编代码如下:

【C++基础】C++模板总结

【C++基础】C++模板总结

2.模板参数匹配

写一行这样的代码的时候:

IsEqual(1, 1.2);

【C++基础】C++模板总结

没有与参数列表匹配的函数模板示例,所以模板参数一定要匹配,不能乱写导致不匹配,不能实例化出代码

3.显示实例化

cout << IsEqual<int>(1, 1.2);
cout << IsEqual<float>(1, 1.2);

上面的IsEqual函数中两个形参的类型是不同的,但时编译器却没有报错,原因是对模板进行了显示实例化

显示示例化是手动的实例化一份代码,类型在函数后面指定,然后直接使用显示实例化出的代码

第一行中1.2被类型转换为整数,第二行是1被转换为浮点型数了

4.重载函数模板

template<typename T>
bool IsEqual(const T& left, const T& right ) {
	return left == right;
}

bool IsEqual(const int& left, const int& right) {
	return left == right;
}

template<typename T1, typename T2>
bool IsEqual(const T1& left, const T2& right) {
	return left == right;
}

void test() {
	cout << IsEqual(1, 2) << endl;
	cout << IsEqual(1, 1.1) << endl;
}

如上代码,模板函数有一个int类型的实例化版本,也有一个两个形参不同版本的模板

三、模板类

类模板是用来生成类的蓝图的,与函数模板不同的是,编译器不能为类模板自动推演模板参数的类型,所以使用类模板的时候必须在模板名后面的尖括号中提供额外的信息。

类模板的格式:

template<class 形参名1, class 形参名2, ... , class 形参名n>

class 类名

{...}

写一个简单的模板类:

template<typename T>
class TestClass {
public:
	TestClass(T t1, T t2) {
		t[0] = t1;
		t[1] = t2;
	}
protected:
	T val[2];
};

如果我们定义一个类对象,想让这个对象存储两个整型数据,可以这样写:

TestClass<int> t(1, 2);

类模板使用时需要显示的实例化,就像上面这样。

如果想要在类外面定义类的成员函数 ,必须在每个函数前面加上template<...>

template<typename T>
class TestClass {
public:
	TestClass(T t1, T t2) {
		t[0] = t1;
		t[1] = t2;
	}
protected:
	T val[2];
};

template<typename T>
T TestClass::GetMaxVal() {
	return val[0] > val[1] ? val[0] : val[1];
}

注意这个函数:

template<typename T>
T TestClass::GetMaxVal()

有一个template<...>和函数,和普通的类外定义成员函数类似,加上函数属于的类域,这里的函数头部的T是返回值