C++之将与参数无关的代码抽离templates(44)---《Effective C++》

来源:互联网 发布:iphone se 知乎 编辑:程序博客网 时间:2024/05/22 08:12

条款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)而造成的代码膨胀,往往可以降低,做法是让带有完全相同的二进制表搜狐的具现类型共享实现码。

阅读全文
0 0