C++ 模板

关于C++的模板。

从知乎上看到的一个评论,颇为赞同:泛型编程”这个概念最早就是来源于C++当初设计STL时所引入的模板(Template),而为什么要引入模板呢,因为STL要完成这样一个目标:设计一套通用的,不依赖类型的,高效的的算法(例如std::sort)和数据结构(例如std::list)。

关于通用性,运行时多态(Polymorphism)可以做到(例如很多高级语言的继承(Inheritance)机制,接口(Interface)机制),但是C++作为一门相对底层的语言,对运行效率的要求是很严格的,而运行时多态会影响效率(例如成员函数只有在运行时才知道调用哪个),所以设计STL的人就创造了一种编译时多态技术,即模板。

那什么又是编译时多态呢,简单点说就是让编译器帮我确定类型,我写程序时只要标记下这里我要用“某种类型”的对象,至于具体是什么类型我不关心,你编译器帮我确定,编译完成后在运行时绝对是类型确定的,这样就大大提高了运行效率,反之对编译就增加了很多工作,而且生成的目标代码也会大大增加。

所以对C++来说,所谓“泛型(Generics)”,并不是说编译器不知道类型,而是针对程序员来说的,这也正是通用性的体现。C++的模板在刚出来的时候并没有想到会演化成今天这样,其他高级语言(如Java,C#)也是看到C++模板在使用的时候带给了程序员极大的便利,就考虑支持这样一种功能,但是也仅仅是借用了C++的模板理念,而没有完全照抄模板的实现方法,所以对于大部分程序员来说,只要使用起来差不多,并不关心实现。所以最后总结下,泛型是只是一个概念,具体实现有C++的模板,Java的泛型等,但实现方法大不相同,只是提供给语言使用者相同的使用方法而已。

简单的用法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//编译时不检查,运行时检查
template<typename T>
void PrintF(T t)
{
std::cout << t << std::endl;
}
//模板类
template<typename T, int N> //你也可以用class 代替typename,但是typename看起来更加符合描述
class Array
{
public:
Array(){}
int GetSize() { return N; }
T operator[](int i) { return arrays[i]; }
private:
T arrays[N];
};
int main()
{
Array<Point, 5> arys; //模板数组类
PrintF(arys.GetSize()); //模板函数输出
}

因为C++类模板机制是“两次编译”,第一次在.h文件生成的函数头,第二次在.cpp文件又生成,所以模板类的实现尽量写在头文件中。

  • C++中模板的声明和实现能分离,只是在主程序中#include的应该是相应的.cpp

  • C++中模板的声明和实现最好不要分开,都写在.h文件,这是因为在多个cpp文件中引用模板参数时可能引起重复定义的编译错误