条款44:将与参数无关的代码抽离templates
template <typename T,std::size_t n>
class SquareMatrix{
public:
...
void invert();
};
现在,考虑这些代码:
SquareMatrix<double,5) sm1;
...
sm1.invert();
SquareMatrix<double,10> sm2;
...
sm2.invert();
这样将会生成两份invert,这些函数并非完全相同,因为其中一个操作的是5*5矩阵而另一个是10*10的矩阵,但除了常量5和10,两个函数的其他部分完全相同,这是tempalte引出代码膨胀的一个典型例子。
可以发现这两个函数完全相同,只除了一个使用5而另一个使用10,那你会怎么做?以5和10来调用这个带参数的函数,而不重复代码。下面我们来进行第一次对SquareMatrix的修改:
策略1:
template <typename T>
class SquareMatrixBase{
protected:
...
void invert(std::size_t matrixSize);
...
};
template <typename T,std::size_t n>
class SquareMatrix:private SquareMatrixBase<T>{
private:
using SquareMatrixBase<T>::invert;
public:
...
void invert(){this->invert(n);}
};
带参数的invert位于base class SquareMatrixBase中,和SquareMatrix一样,SquareMatrixBase也是个template,不同的是它只对“矩阵元素对象的类型”参数化,不对矩阵的尺寸参数化,因此对于某给定之元素对象那个类型,所有矩阵共享同一个(也是唯一一个)SquareMatrixbase class,它们也将因此共享这唯一一个class内的invert。注意这些函数中我们使用“this->”标记,因为如果不这样做,便如上篇博客中所讲的那样,模板化基类内的函数名称会被derived class掩盖,同时我们这儿使用private继承的原因是base class SquareMatrixBase只是为了帮助derived class SquareMatrix是实现,子类和父类之间不是“is-a”关系。
策略2:
针对SquareMatrixBase::invert的实现,我们该如何操作呢?解决办法是让SquareMatrixBase贮存一个指针,指向矩阵数值所在的内存,而只要它存储了这些东西,也就可能存储矩阵尺寸,如下:
template <typename T>
class SquareMatrixBase{
protected:
SquareMatrixBase(std::size_t n,T* pMem):size(n),pDate(pMem){
}
void setDataPtr(T* ptr){pData=ptr;}
...
private:
std::size_t size;
T* pData;
};
这允许derived class中决定内存分配方式,这里我们针对derived class有两种分配方式:
1)存储指针数组,这样可能导致对象吱声超级大!!!
template <typename T,std::size_t n>
class SquareMatrix:private SquareMatrixBase<T>{
public:
SquareMatrix():SquareMtrixBase<T>(n,data){}
...
private:
T data[n*n];
};
2)把每一个矩阵的数据放进heap中,即通过new进行动态内存分配;
template <typename T,std::size_t n>
class SquareMatrix:private SquareMatrixBase<T>{
public:
SquareMatrix():SquareMatrixBase<T>(n,0),pData(new T[n*n]){
this->setDataPtr(pData.get());
}
...
private:
boost::scoped_array<T> pData;
};
PS:
1)non-type template parameters(非类型模板参数):模板参数并不局限于类型,template <<>typename T,int n>或者template <<>typename T,size_t n>等等,第二个就是非类型模板参数!
2)type parameters:标准的类型模板参数,template <<>typename T>。
总结:
1)template生成多个classes和多个函数,所以任何template代码都不应该与某个造成膨胀的template参数产生相依关系;
2)因类型模板参数(non-type template parameters)造成的代码膨胀,往往可以消除,做法是以函数参数或者class成员变量代替template参数;
3)因类型参数(type parameters)而造成的代码膨胀,往往可以降低,做法是让带有完全相同的二进制表搜狐的具现类型共享实现码。