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 = ptr;
	}
private:
	size_t size;
	T* pData;
};

template<typename T, size_t n>
class SquareMatrix : private SquareMatrixBase<T> {
public:
	SquareMatrix() : SquareMatrixBase(n, data) {}
private:
	T data[n*n];
};

还有一种改法:

template<typename T>
class SquareMatrixBase {
protected:
	SquareMatrixBase(size_t n, T* pMem) : size(n), pData(pMem) {}
	void setDataPtr(T* ptr) {
		pData = ptr;
	}
private:
	size_t size;
	T* pData;
};

template<typename T, size_t n>
class SquareMatrix : private SquareMatrixBase<T> {
public:
	SquareMatrix() : SquareMatrixBase<T>(n, 0),  //base class的数据指针设为null
		pData(new T[n*n])   //为内容分配内存,将指向该内存的指针存储起来
	{
		this->setDatePtr(pDate.get()); //将pData的一个副本交给base class
	}
private:
	boost::scoped_array<T> pData;
};

以上这种做法是把每个矩阵的数据放进heap。

请记住:

(1)Template生成多个classes和多个函数,所以任何template代码都不该与某个造成膨胀的template参数产生相依关系。

(2)因非类型模版参数而造成的代码膨胀,往往可消除,做法是以函数参数或class成员变量替换template参数。

(3)因类型参数而造成的代码膨胀,往往可降低,做法是让带有完全相同二进制表述的具现类型共享实现码。

时间: 2024-08-25 02:24:29

Effective C++:条款44:将与参数无关的代码抽离template的相关文章

Effective C++ 条款44 将与参数无关的代码抽离template

1. template是节省时间和避免代码重复的有效方法,而且在将类模板(class template)具现化时,编译器只具现化那些被用到的成员函数,这更加节省了空间. 2. 正如两个函数存在代码重复时,倾向于把重复的代码抽出独立形成一个函数,然后让之前的函数调用这个函数,函数模板也可以这样,甚至类模板也可以采用相同的思想,例如,对于以下用于操作方矩阵的类模板: template<typename T,std::size_t n> //n是非类型参数 class SquareMatrix{ p

Effective C++ Item 44 将与参数无关的代码抽离 templates

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:Templates 生成多个 classes 和多个函数,所以任何 template 代码都不该与某个造成膨胀的 template 参数产生相依关系 因非类型模板参数(non-type template parameters) 而造成的代码膨胀,往往可消除,做法是以函数参数或 class 成员变量替换 template 参数 示例: template<typename T, std:

读书笔记 effective c++ Item 44 将与模板参数无关的代码抽离出来

1. 使用模板可能导致代码膨胀 使用模板是节省时间和避免代码重用的很好的方法.你不需要手动输入20个相同的类名,每个类有15个成员函数,相反,你只需要输入一个类模板,然后让编译器来为你实例化20个特定的类和300个你需要的函数.(只有在被使用的情况下类模版的成员函数才会被隐式的实例化,所以只有在300个函数被实际用到的情况下才会生成300个成员函数.)函数模板同样吸引人.你不用手动实现许多函数,你只需要实现一个函数模板,然后让编译器来做余下的事情. 然而在有些时候,如果你不小心,使用模板会导致代

Item 44:将参数无关代码重构到模板外去

Item 44: Factor parameter-independent code out of templates. 模板是个好东西,你可以在实现类型安全的同时少写很多代码.但模板提供的是编译期的多态, 即使你的代码看起来非常简洁短小,生成的二进制文件也可能包含大量的冗余代码. 因为模板每次实例化都会生成一个完整的副本,所以其中与模板参数无关的部分会造成代码膨胀(code bloat). 把模板中参数无关的代码重构到模板外便可以有效地控制模板产生的代码膨胀. 另外代码膨胀也可以由类型模板参数

Effective C++ 条款24 若所有参数皆需类型转换,请为此采用non-member函数

1. 通常,将只接受拷贝构造函数声明为explict是一个不错的主意,因为这可以避免自动的隐式类型转换所带来的错误,但有些情况下,使类支持隐式类型转换是可行的,例如自定义的数值类型: class Rational{ public: Rational(int numerator=0,int denominator=1); int numerator()const; int denominator()const; private: int numerator; int denominator; }

Effective C++ Item 44 将与參数无关的代码抽离 templates

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:Templates 生成多个 classes 和多个函数,所以不论什么 template 代码都不该与某个造成膨胀的 template 參数产生相依关系 因非类型模板參数(non-type template parameters) 而造成的代码膨胀,往往可消除,做法是以函数參数或 class 成员变量替换 template 參数 演示样例: template<typename T,

Effective C++ 条款总结

自己在看这本书的时候,回去翻看目录的时候,有些规则会被遗忘,因此做个简单的小总结供自己和其他人参考,没读过的还是要先去读一遍的 一.让自己习惯C++ 1.视C++为一个语言联邦 C++是一种包含许多特性的语言,因而不要把它视为一个单一语言.理解C++至少需要学习一下4个部分: ①C语言.C++仍以C为基础 ②objected-oriented C++.面向对象编程,类.封装.继承.多态 ③template C++.C++泛型编程.模板元编程的基础 ④STL.容器.迭代器.算法 2.尽量使用con

More Effective C++ 条款34 如何在一个程序中结合C++和C

1. C++和C混合使用的前提之一就是编译器产生兼容的目标文件(.lib和.dll等).所谓"兼容",指的是编译器在"预编译器相依的特性上"一致,如int和double大小,参数压栈机制等,只有在这个基础上才能讨论结合使用C++和C模块的问题. 2. 在1的基础上,要结合使用C++和C的模块,主要有以下几点需要注意: 1). name mangling(名称重整) Name mangling是C++用于支持函数重载的机制,它对重载的函数名称进行一定改变,使得每个函数

Effective C++ 条款3 尽可能用const

1. const可被施加于任何作用域内的对象,函数参数,函数返回类型,成员函数本体.用const修饰指针,如果const出现在*之前,表明指针不能更改所指向的对象的内容,如果const出现在*之后,表明指针只能指向同一块内存.另外int const*p和const int*p含义相同.如果对象成员有普通指针,那么构造该类的一个const对象时,const修饰使得该指针只能指向同一块内存,但指针指向的内容可以改变. 2. 将某些东西声明为const可以帮助编译器侦测出错误用法. 3. 编译器强制实