4.2.2 实例化时间(1)
随着模板被广泛地使用,C++(www.cppentry.com)程序将在实例化模板上面花费更多的创建时间;而且,对于某些C++(www.cppentry.com)用户而言,如何减少模板实例化时间已经成为一个亟待解决的问题。开发团队也非常注重创建时间,因为他们总是编译程序、运行测试程序、调试程序、改变程序的某些部分并再次编译等。因此,对用户而言,最小化模板实例化时间已经成为编写可重用代码的一个很重要的目标了。
1.简单的技术
下面两种简单的技术可以用来减少实例化时间:程序库预实例化模板(或者预实例化某些模板)和定义函数模板为内联(inline)。其中,在库文件中预实例化模板是减少程序库用户实例化时间最明显的方法。例如,假设我们提供了3.2.1节的二分查找树模板:
- template<class T>
- class BSTree{
- //...
- };
如果在程序库的实现中我们使用了BSTree<String>,或者发现大多数用户都创建了BSTree<String>,那么我们就可以预实例化BSTree<String>,并把实例化后的结果文件放入库档案文件里面。这种预实例化模板机制是高度依赖于操作系统的,我们将在9.7节讨论这个问题。
某些模板必须在程序库内部预实例化。假设我们在程序库内部使用了BSTree<Private>,其中Private是一个只在这个程序库实现中使用的类型,并且在任何程序库的公共头文件中都不提供这个类型。于是,如果用户创建了一个使用这个程序库的应用程序,那么实例化器将找不到Private类型的实现,由于Private类型上面的性质,用户根本就不能实例化BSTree<Private>,而必须由程序库自己对它进行预实例化。
在大多数系统中,另一个减少用户实例化时间的技术就是定义函数模板为内联的:
- template<class T>
- class BSTree {
- int _size;
- public;
- int size() const {return _size;}
- //...
- };
在上面代码中,size函数返回树中节点的数目,其中节点的数目存储在成员变量_size中。size函数由于被声明为内联函数,所以编译期间在每次调用的地方,它的实例化速度将非常地快,这也是大多数系统实例化函数的最快方式。当然,这个技术只适用于那些可以声明为内联的函数。
通常,对于任何编程(www.cppentry.com)技术,只要能够减少实例化代码的大小,它就可以减少实例化时间。下面的两个技术是hoisting(即提升,指在原来类体系的基础上,声明一个非模板的基类)和使用指针容器(pointer containers)。
2.Hoisting(提升)
假设由于参数的原因,BSTree::size函数不能被定义为内联函数:
- template<class T>
- class BSTree ]
- public;
- int size() const;
- //...
- };
- template<class T>
- int BSTree<T>::size() const {
- return_size;
- }
然而,上面size函数的实现并不依赖于BSTree的模板参数。理论上讲,C++(www.cppentry.com)编译系统应该能够发现:size函数的所有特殊化目标代码(即由于模板参数不同引起的不同代码)都是一样的,从而编译系统只实例化了size函数的一个特例;但现今的编译系统几乎都不能够执行这种优化。于是,我们可以通过hoisting(提升)size函数的定义到一个新创建的非模板基类里,来高效地执行这种优化:- class BSTreebase { //用于提升的非模板类
- public:
- int size() const;
- protected:
- int_size;
- };
- int BSTreebase::size() const {
- return_size;
- }
- template<class T>
- class BSTree : public BSTreebase {
- //现在是继承自基类的size函数
- //...
- };
我们还必须同时提升成员变量_size:而且,我们把_size声明为protected类型,是因为模板类BSTree的派生类可能需要存取这个变量。