分享
 
 
 

翻译:Effective C++, 3rd Edition, Item 44: 从 templates(模板)中分离出 parameter-independent(参数无关)的代码(上)

王朝c/c++·作者佚名  2006-01-10
窄屏简体版  字體: |||超大  

Item 44: 从 templates(模板)中分离出 parameter-independent(参数无关)的代码

作者:Scott Meyers

译者:fatalerror99 (iTePub's Nirvana)

发布:http://blog.csdn.net/fatalerror99/

templates(模板)是节省时间和避免代码重复的极好方法。不必再输入 20 个相似的 classes,每一个包含 15 个 member functions(成员函数),你可以输入一个 class template(类模板),并让编译器实例化出你需要的 20 个 specific classes(特定类)和 300 个函数。(class template(类模板)的 member functions(成员函数)只有被使用时才会被隐式实例化,所以只有在每一个函数都被实际使用时,你才会得到全部 300 个member functions(成员函数)。)function templates(函数模板)也有相似的魅力。不必再写很多函数,你可以写一个 function templates(函数模板)并让编译器做其余的事。这不是很重要的技术吗?

是的,不错……有时。如果你不小心,使用 templates(模板)可能导致 code bloat(代码膨胀):重复的(或几乎重复的)的代码,数据,或两者都有的二进制码。结果会使源代码看上去紧凑而整洁,但是目标代码臃肿而松散。臃肿而松散很少会成为时尚,所以你需要了解如何避免这样的二进制扩张。

你的主要工具有一个有气势的名字 commonality and variability analysis(通用性与可变性分析),但是关于这个想法并没有什么有气势的东西。即使在你的职业生涯中从来没有使用过模板,你也应该从始至终做这样的分析。

当你写一个函数,而且你意识到这个函数的实现的某些部分和另一个函数的实现本质上是相同的,你会仅仅复制代码吗?当然不。你从这两个函数中分离出通用的代码,放到第三个函数中,并让那两个函数来调用这个新的函数。也就是说,你分析那两个函数以找出那些通用和变化的构件,你把通用的构件移入一个新的函数,并把变化的构件保留在原函数中。类似地,如果你写一个 class,而且你意识到这个 class 的某些构件和另一个 class 的构件是相同的,你不要复制那些通用构件。作为替代,你把通用构件移入一个新的 class 中,然后你使用 inheritance(继承)或 composition(复合)(参见 Items 323839)使得原来的 classes 可以访问这些通用特性。原来的 classes 中不同的构件——变化的构件——仍保留在它们原来的位置。

在写 templates(模板)时,你要做同样的分析,而且用同样的方法避免重复,但这里有一个技巧。在 non-template code(非模板代码)中,重复是显式的:你可以看到两个函数或两个类之间存在重复。在 template code(模板代码)中。重复是隐式的:仅有一份 template(模板)源代码的拷贝,所以你必须培养自己去判断在一个 template(模板)被实例化多次后可能发生的重复。

例如,假设你要为固定大小的 square matrices(正方矩阵)写一个 templates(模板),其中,要支持 matrix inversion(矩阵转置)。

template<typename T, // template for n x n matrices of

std::size_t n> // objects of type T; see below for info

class SquareMatrix { // on the size_t parameter

public:

...

void invert(); // invert the matrix in place

};

这个 template(模板)取得一个 type parameter(类型参数)T,但是它还有一个类型为 size_t 的参数——一个 non-type parameter(非类型参数)。non-type parameter(非类型参数)比 type parameter(类型参数)更不通用,但是它们是完全合法的,而且,就像在本例中,它们可以非常自然。

现在考虑以下代码:

SquareMatrix<double, 5> sm1;

...

sm1.invert(); // call SquareMatrix<double, 5>::invert

SquareMatrix<double, 10> sm2;

...

sm2.invert(); // call SquareMatrix<double, 10>::invert

这里将有两个 invert 的拷贝被实例化。这两个函数不是相同的,因为一个作用于 5 x 5 矩阵,而另一个作用于 10 x 10 矩阵,但是除了常数 5 和 10 以外,这两个函数是相同的。这是一个发生 template-induced code bloat(模板导致的代码膨胀)的经典方法。

如果你看到两个函数除了一个版本使用了 5 而另一个使用了 10 之外,对应字符全部相等,你该怎么做呢?你的直觉让你创建一个取得一个值作为一个参数的函数版本,然后用 5 或 10 调用这个参数化的函数以代替复制代码。你的直觉为你提供了很好的方法!以下是一个初步过关的 SquareMatrix 的做法:

template<typename T> // size-independent base class for

class SquareMatrixBase { // square matrices

protected:

...

void invert(std::size_t matrixSize); // invert matrix of the given size

...

};

template< typename T, std::size_t n>

class SquareMatrix: private SquareMatrixBase<T> {

private:

using SquareMatrixBase<T>::invert; // avoid hiding base version of

// invert; see Item 33

public:

...

void invert() { this->invert(n); } // make inline call to base class

}; // version of invert; see below

// for why "this->" is here

就像你能看到的,invert 的参数化版本是在一个 base class(基类)SquareMatrixBase 中的。与 SquareMatrix 一样,SquareMatrixBase 是一个 template(模板),但与 SquareMatrix 不一样的是,它参数化的仅仅是矩阵中的对象的类型,而没有矩阵的大小。因此,所有持有一个给定对象类型的矩阵将共享一个单一的 SquareMatrixBase class。从而,它们共享 invert 在那个 class 中的版本的单一拷贝。

SquareMatrixBase::invert 仅仅是一个计划用于 derived classes(派生类)以避免代码重复的方法,所以它是 protected 的而不是 public 的。调用它的额外成本应该为零,因为 derived classes(派生类)的 inverts 使用 inline functions(内联函数)调用 base class(基类)的版本。(这个 inline 是隐式的——参见 Item 30。)这些函数使用了 "this->" 标记,因为就像 Item 43 解释的,如果不这样,在 templatized base classes(模板化基类)中的函数名(诸如 SquareMatrixBase<T>)被 derived classes(派生类)隐藏。还要注意 SquareMatrix 和 SquareMatrixBase 之间的继承关系是 private 的。这准确地反映了 base class(基类)存在的理由仅仅是简化 derived classes(派生类)的实现的事实,而不是表示 SquareMatrix 和 SquareMatrixBase 之间的一个概念上的 is-a 关系。(关于 private inheritance(私有继承)的信息,参见 Item 39。)

迄今为止,还不错,但是有一个棘手的问题我们还没有提及。SquareMatrixBase::invert 怎样知道应操作什么数据?它从它的参数知道矩阵的大小,但是它怎样知道一个特定矩阵的数据在哪里呢?大概只有 derived class(派生类)才知道这些。derived class(派生类)如何把这些传达给 base class(基类)以便于 base class(基类)能够做这个转置呢?

一种可能是为 SquareMatrixBase::invert 增加另一个的参数,也许是一个指向存储矩阵数据的内存块的开始位置的指针。这样可以工作,但是十有八九,invert 不是 SquareMatrix 中仅有的能被写成一种 size-independent(大小无关)的方式并移入 SquareMatrixBase 的函数。如果有几个这样的函数,全都需要一种找到持有矩阵内的值的内存的方法。我们可以为它们全都增加一个额外的参数,但是我们一再重复地告诉 SquareMatrixBase 同样的信息。这看上去不太正常。

(未完待续)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有