首页 > 代码库 > Effective C++:条款44:将与参数无关的代码抽离template
Effective C++:条款44:将与参数无关的代码抽离template
(一)
template是节省时间和避免重复代码的一个奇妙方法。class template的成员函数只有在被使用时才被暗中具现化。function templates有类似的诉求。
但是如果你不小心,使用templates可能导致代码膨胀(code bloat):其二进制代码带着重复(或几乎重复)的代码、数据、或两者。其结果可能源码看起来合身整齐,但目标码却不是那么回事。你需要知道如何避免这样的二进制浮夸。
主要工具是:共性与变性分析。
在non-template中,重复十分明确,然而在template中,重复是隐晦的:你必须训练自己去感受当template被具现化多次时可能发生的重复。
举例,矩阵template,支持矩阵inversion运算:template<typename T, std::size_t n> class SquareMatrix{ public: void invert(); };
template接受一个类型参数T,还有一个类型为size_t的非类型参数(non-type parameter)。
现在考虑,以下情况:
SquareMatrix<double, 5> sm1; sm1.invert(); SquareMatrix<double, 10> sm2; sm2.invert();这会具现两份invert。这些函数并非完全相同,但除了常量5和10,其他部分都相同,这是template引出代码膨胀的一个典型例子。
所以我们要对它进行修改!
(二)
我们先把它修改成如下形式:
template<typename T> //与尺寸无关的base class class SquareMatrixBase { protected: void invert(size_t matrixSize); //以给定的尺寸求逆矩阵 }; template<typename T, size_t n> class SquareMatrix : private SquareMatrixBase<T> { private: using SquareMatrixBase<T>::invert; //避免遮掩base版的invert public: void invert() { this->invert(n); //制造一个inline调用,用this->为了不被derived classes的函数名称掩盖 } };带参数的invert位于base class中。和SquareMatrix一样,也是个template,不同的是他只对“矩阵元素对象的类型”参数化,不对矩阵的尺寸参数化。因此对于给定的元素对象类型,所有矩阵共享同一个(也是唯一一个)SquareMatrixBase class。也将因此而共享这唯一一个class内的invert。
目前为止一切都好,但是SquareMatrixBase::invert如何知道该操作什么数据?想必只有derived class知道。一个可能的做法是为SquareMatrixBase::invert添加一个新的参数,也许是个指针,指向一块用来放置矩阵数据的内存起始点。那行的通,但是十之八九invert不是唯一一个可写为“形式与尺寸无关并可移至SquareMatrixBase内”的SquareMatrix函数。如果有若干这样的函数,我们可以对所有这样的函数添加一个额外参数,却得一次次地告诉SquareMatrixBase相同的信息,这样不好。
(三)
我们尝试另外一种办法:
令SquareMatrixBase贮存一个指针,指向矩阵数值所在的内存。而只要它存储了那些东西,也就可能存储矩阵尺寸:
这允许derived classes决定内存分配方式。某些实现版本也许会将矩阵数据存储在SquareMatrix对象内部:
template<typename T> class SquareMatrixBase { protected: SquareMatrixBase(size_t n, T* pMem) : size(n), pData(pMem) {} void setDataPtr(T* ptr) { pData = http://www.mamicode.com/ptr;>
还有一种改法:template<typename T> class SquareMatrixBase { protected: SquareMatrixBase(size_t n, T* pMem) : size(n), pData(pMem) {} void setDataPtr(T* ptr) { pData = http://www.mamicode.com/ptr;>以上这种做法是把每个矩阵的数据放进heap。
请记住:
(1)Template生成多个classes和多个函数,所以任何template代码都不该与某个造成膨胀的template参数产生相依关系。
(2)因非类型模版参数而造成的代码膨胀,往往可消除,做法是以函数参数或class成员变量替换template参数。
(3)因类型参数而造成的代码膨胀,往往可降低,做法是让带有完全相同二进制表述的具现类型共享实现码。
Effective C++:条款44:将与参数无关的代码抽离template