信奥竞赛

系列

剑指信奥 | C++ 之模版

剑指信奥 | C++ 之模版

泛型编程

泛型编程 generic programming 是 C++ 语言所支持的另外一种非常有用的编程方式:

泛型程序设计(generic programming)是程序设计语言的一种风格或范式。

泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型。

Wikipedia

泛型这个词不是日常用语,不是很好理解,如果用通俗的语言来解释,就是泛化、通常的含义,它所指代的泛化的含义一般是针对于 参数的数据类型 而言的。

这部分内容涉及了函数,C++ 的函数与 C 语言是一致的,如果不了解函数,可以参看 C 语言的函数:

剑指信奥 | C 语言之一劳永逸的函数

我们先来看一个不是泛型的简单函数的例子:

#include <iostream>

using namespace std;


int add(int x, int y) {
return x + y;
}

int main() {

int i1 = 1, i2 = 2;
cout << "i1 + i2 = " << add(i1, i2) << endl;

double d1 = 1.2, d2 = 3.4;
cout << "d1 + d2 = " << add(d1, d2) << endl;

return 0;
}

/*
output:
i1 + i2 = 3
d1 + d2 = 4
*/

我们先是定义了一个简单的求和函数 add,注意这个函数的两个参数以及返回类型都是整型。

然后我们在主函数中定义两个整型变量和两个浮点型变量,分别传入求和函数进行计算。

虽然没有编译错误,输出了结果,但是只有整型参数的结果是正确的,浮点型的结果是错误的。

当然,我们可以再定义一个接收浮点型参数的函数来解决这个问题,但是明显增加了很多代码,而且似乎不太必要:新函数的逻辑跟之前函数是一致的,仅仅是参数和返回类型不同。

于是,聪明的人们想到了办法,就是使用泛型解决这个问题,现在我们就看看,在 C++ 中,这个问题是怎么解决的。

模版

泛型编程具体到 C++ 语言中,被称为 模版 template,只是一个称呼上的不同。

现在,我们就使用模版函数解决这个问题,看代码示例:

#include <iostream>

using namespace std;

// 模版函数
template<typename T>
T add(T x, T y) {
return x + y;
}

int main() {

int i1 = 1, i2 = 2;
cout << "i1 + i2 = " << add(i1, i2) << endl;

double d1 = 1.2, d2 = 3.4;
cout << "d1 + d2 = " << add(d1, d2) << endl;

return 0;
}

/*
output:
i1 + i2 = 3
d1 + d2 = 4.6
*/

仅仅在添加了一行代码 template<typenam T> 之后,我们把原来的三处 int 改成了 T,问题就得到了完善的解决,而这就是模版的作用。

回头再看下泛型的定义,就很好理解了:在函数定义之初,泛型并不指代确切的类型,在使用时才会具体化为某一种类型。

使用泛型编程实现了代码的重用,可以简化编程。

C++ 中不仅可以定义模版函数,还可以声明模版类,模版的使用在 C++ 中十分常见。

虽然我们简单实现了一个简单的模版,但现实中,尤其是在信息学竞赛中,我们基本上不需要自己定义模版函数或模版类,甚至不需要声明任何的类。

但是,类和模版的原理我们是需要理解的,因为这些原理涉及到了一个重要的内容 — C++ 的 STL,它叫做 C++ 标准模版库,这是我们 C++ 最后一部分重要内容,我们从下一课开始花大量篇幅重点学习这部分知识。