《Effective C++》学习笔记——条款44
来源:互联网 发布:c语言长整型数 编辑:程序博客网 时间:2024/06/05 08:20
七、模板与泛型编程
条款44:将与参数无关的代码抽离templates
template
Template是节省时间和避免代码重复的一个奇妙的方法。
你只需要键入一个class template,剩下的就可以留给编译器去具现化那些相关类和函数。
- class templates的成员函数只有在被使用时才被暗中具现化,所以只有被具现化的所有函数都使用一遍,你才会获得所有函数。
Function templates也差不多,只需要写一个function template,然后就可以让编译器去干剩下的活了。
但是,使用templates可能会导致代码膨胀:其二进制码带着重复的代码、数据或两者都有。
所以,需要知道如何避免这样的错误 —— 共性与变性分析
- 对于函数:
分析两个函数,找出共同的部分和变化的部分,把共同的部分搬到一个新函数去,保留了两个函数,找出共同的部分和变化的部分,把共同部分搬到一个新函数去,保留变化的部分在原函数中不动。 - 对于类:
同函数,令原先的classes取用共同特性,而原classes的互异部分仍然在原位置不动。 - 对于template:
在non-template代码中,重复十分明确:你可以看到两个函数或两个classes之间有所重复;在template代码中,重复隐晦的,毕竟只存在一份template源码,所以必须训练自己去感受当template被具现化多次时可能发生的重复。
一个例子
想为固定尺寸的正方矩阵编写一个template。该矩阵的性质之一是支持矩阵的逆运算。
// template支持 n x n 矩阵,元素是类型为T的objectstemplate<typename T, std::size_t n> class SquareMatrix {public: ... void invert(); // 求逆矩阵};
这个template接受一个类型参数T,除此之外还接受一个类型为size_t的参数(这是一个非类型参数)。
然后,来看一下应用:
SquareMatrix<double, 5> sm1;...sm1.invert();SquareMatrix<double, 10> sm2;...sm2.invert();
这段代码将具现化两份invert,但这两份invert除了常量5和10,其他部分完全相同。
这种template代码膨胀非常典型。
对template代码膨胀的修改
- 第一次优化修改
为它们建立一个带数值参数的函数,然后以5和10来调用这个带参数的函数,从而不重复代码。
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; // 避免遮掩base版的invertpublic: ... void invert() { this->invert(n); } // 制造一个inline调用,调用base class版的invert};
SquareMatrixBase::invert
只是企图成为”避免derived classes代码重复”的一种方法,所以它以protected替换public
。- 这些函数使用
this->
记号,因为若不这样做,便如条款43所说,模板化基类内的函数名称会被derived classes
掩盖。 SquareMatrix和SquareMatrixBase
之间的继承关系是private。这反应一个事实:这里的base class只是为了帮助derived classes实现,不是为了表现SquareMatrix和SquareMatrixBase
之间的is-a关系。- 缺陷:
SquareMatrixBase::invert
如何知道该操作什么数据?虽然它从参数中知道矩阵尺寸,但它如何知道哪个特定矩阵的数据在哪里呢。 - 参考:
一个可能的做法是为SquareMatrixBase::invert
添加另一个参数,也许是个指针,指向一块用来放置矩阵数据的内存起始点。那行得通,但十之八九invert不是唯一一个可写为”形式与尺寸无关并可移至SquareMatrixBase内”的”SquareMatrix函数。如果有若干这样的函数,我们唯一要做的就是找出保存矩阵元素值的那块内存。我们可以对所有这样的函数添加一个额外参数,却得一次又一次地告诉SquareMatrixBase相同的信息,这样做不是很好。
2. 第二次修改
令SquareMatrixBase贮存一个指针,指向矩阵数值所在的内存。而只要它存储了那些东西,也就可能存储矩阵的尺寸。
template<typename T>class SquareMatrixBase {protected: SquareMatrixBase(std::size_t n, T* pMenu) :size(n), pData(pMem) { } void setDataPtr(T* ptr) { pData = ptr; } ...private: std::size_t size; T* pData;};
这允许derived class决定内存的分配方式,某些实现版本也许会决定将矩阵数据存储在SquareMatrix对象内部:
template<typename T, std::size_t n>class SquareMatrix: private SquareMatrixBase<T> {public: SquareMatrix(): SquareMatrixBase<T>(n, data) // 送出矩阵大小和数据指针给base class { } ...private: T data[n*n];};
这种类型的对象就不需要动态分配内存,但对象自身可能非常大。另一种做法就是把每一个矩阵的数据放进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]) // 将base class的数据指针设为null,为矩阵内容分配内存 { this->setDataPtr(pData.get()); } // 将指向该内存的指针存储起来,然后将它的一个副本交给base class ...private: boost::scoped_array<T> pData;};
硬是绑着矩阵尺寸的那个invert版本,有可能生成比共享版本(其中尺寸乃以函数参数传递或存储在对象内)更佳的代码。
从另一个角度看,不同大小的矩阵只拥有单一版本的invert,可减少执行文件大小,也就因此降低程序的working set(指对一个在”虚内存环境”下执行的进程而言,其所使用的那一组内存页)大小,并强化指令高速缓存区内的引用集中化。
本条款只讨论由non-type template parameters(非类型模板参数)带来的膨胀,其实type parameters(类型参数)也会导致膨胀。
请记住
- Templates生成多个classes和多个函数,所以任何template代码都不该与某个造成膨胀的template参数产生相依关系。
- 因非类型模板参数(non-type template parameters)而造成的代码膨胀,往往可消除,做法是以函数参数或class成员变量替换template参数。
- 因类型参数(type parameters)而造成的代码膨胀,往往可降低,做法是让带有完全相同二进制表述(binary representations)的具现类型(instantiation types)共享实现码。
- 《Effective C++》学习笔记——条款44
- 《Effective C++》学习笔记——条款15
- 《Effective C++》学习笔记——条款16
- 《Effective C++》学习笔记——条款17
- 《Effective C++》学习笔记——条款18
- 《Effective C++》学习笔记——条款19
- 《Effective C++》学习笔记——条款20
- 《Effective C++》学习笔记——条款21
- 《Effective C++》学习笔记——条款22
- 《Effective C++》学习笔记——条款23
- 《Effective C++》学习笔记——条款24
- 《Effective C++》学习笔记——条款25
- 《Effective C++》学习笔记——条款26
- 《Effective C++》学习笔记——条款27
- 《Effective C++》学习笔记——条款28
- 《Effective C++》学习笔记——条款29
- 《Effective C++》学习笔记——条款30
- 《Effective C++》学习笔记——条款31
- 树莓派使用 RTL8188CUS芯片 开AP模式
- MFC CWinApp Class成员变量及成员函数
- 常见的CSS布局总结
- html基础
- 赛意 右移
- 《Effective C++》学习笔记——条款44
- QT的显示机制
- ionic之样式bar-dark
- 【并行计算-CUDA开发】CUDA ---- Warp解析
- 09. Swing Interfaces
- 关于JVM内存区域划分的一些认识
- JavaScript arguments 对象全面介绍
- python 调用c库
- [kuangbin带你飞]专题四 最短路练习 P HDU 4725