Effective C++
___________________________________________________
class Bird
{
public:
virtual void fly();
...
};
class penguin :public Bird
{
...
};
这个时候我们遇上了乱流,因为这个继承体系说企鹅可以飞,而我们知道那不是真的,怎么回事?现在开始谨慎一点,我们应该
class Bird
{
... //没有声明fly函数
};
class FlyingBird :public Bird
{
public:
virtual void fly();
...
};
class penguin :public Bird
{
... //没有声明fly函数
};
即便如此,此刻我们仍然未能完全处理好这些鸟事,世界并不存在一个"适用于所有软件"的完美设计.所谓最佳设计,取决于系统希 望
void error(const string& msg); //定义于某处
class Penguin :public Bird
{
public:
virtual void fly()
{
error("Attempt to make a penguin fly!");
}
.....
};
很重要的是,你必须认知这里所说的某些东西可能和你所想的不同.这里并不是说"企鹅不会飞",而是说"企鹅会飞,但尝试那么 做是
总结:
1.virtual函数的替代方案包括 NVI方法 及 Strategy设计模式的多种形式。NVI方法 自身是一个特殊形式的 Template Method 设
计模式。
2.将机能从成员函数移到class外部函数,带来的一个缺点是,非成员函数无法访问class内的 non-public成员。
3.tr1::function 对象的行为就像一般的函数指针。这样的对象可接纳“与给定的目标签名式兼容”的所有可调用物。
假设我告诉你,class D系由class B以public形式派生而来,class B定义有一个public成员函数mf,由于mf的参数和返回值都不重
要,所以假设两者皆为void.换句话讲我的意思是:
class B{
public:
void mf();
...
};
class D:public B
{....};
虽然我们对B,D和mf一无所知,但面对一个类型为D的对象x:
D x; //x是一个类型为D的对象
如果以下行为:
B* pB = &x; //获得一个指针指向x
pB->mf(); //经由该指针调用mf
异于以下行为:
D* pD = &x; //获得一个指针指向x
pD->mf(); // 经由该指针调用mf
你可能会相当惊讶.毕竟两者都通过对象x调用成员函数mf.由于两者所调用的函数都相同,凭借的对象也相同,所以行为也应该相同,是吗?
是的,理应如此,但事实可能不是如此.更明确的说,如果mf是个non-virtual函数而D定义有一个自己的mf版本,那就不是如此:
class D :public B
{
public:
void mf(); //hides 隐藏了B::mf
....
};
pB->mf(); //调用B::mf
pD->mf(); //调用D::mf
造成此一两面行为的原因是,non-virtual函数如B::mf和D::mf都是静态绑定. 这个意思就是,由于pB被声明为一个 Pointer-to-
但另一方面,virtual函数确实动态绑定,所以他们不受这个问题苦恼.如果mf为一个virtual函数,不论是通过pB还是pD调用mf都会
导致调用D::mf,因为pB和pD真正指的都是一个类型为D的对象.
如果你正在编写class D并重新定义继承自class D的non-virtual函数mf,D对象很可能展现出精神分裂的不一致行径.更明确的说,当
mf调用,任何一个D对象都可能展现出B或D的行为;决定因素不在对象本身,而在于"指向该对象之指针"当初的声明类型.Reference也
会展现和指针一样难以理解的行径.
条款32教会我们如果是public继承,那么就要符合is-a关系. 那么:
1.适用于B对象的每一件事情,也同样适用于D对象,因为D对象都是一个B对象;
2.B的derived classes一定会继承md的接口和实现,因为mf是B的一个non-virtual函数.
现在,如果D重新定义mf,你的设计就会出现矛盾,不论从任何情况下,都不应该重新定义一个继承而来的non-virtual函数.
最后总结: 绝对不要重新定义继承而来的non-virtual函数.
条款37:绝不重新定义继承而来的缺省参数值
让我们一开始就将讨论简化.你只能继承两种函数:virtual和non-virtual函数.然而重新定义一个继承而来的non-virtual函数永远
都是错的,所以我们可以安全地将本条款的讨论局限于"继承一个带有缺省参数值的virtual函数"
这种情况下,本条款成立的理由就非常直接而明确了:virtual函数系动态绑定,而缺省参数值却是静态绑定.
何为动态绑定和静态绑定????? 浅析静态绑定和动态绑定 这里面有你想要的答案.
总结:
绝对不要重新定义一个继承而来的缺省参数值,因为缺省参数值都是静态绑定,
而virtual函数-你唯一应该覆写的东西-却是动态绑定.
条款38:通过复合塑模出has-a或者"根据某物实现出"
1.复合的意义和public继承完全不同
2.在应用域,复合以为has-a.在实现域,复合意味is-implemented-in-terms-of
条款39:明智而审慎地使用private继承
1.private继承意味is-implemented-in-trems of(根据某物实现出).它通常比复合的级别低.但是当derived class需要访问
protected base class的成员,或需要重新定义继承而来的virtual函数时,这么设计是合理的.
2.和复合不同,private继承可以造成empty base最优化. 这对致力于对象尺寸最小化""的程序库开发者而言,可能很重要.
一旦涉及多重继承,C++社群便分为两个基本阵营.其中之一认为如果单一继承是好的,多重继承一定是更好.另一阵营则主张,单
一继承是好的多重继承不值得拥有.他们两个阵营都是会有自己支撑的理由,下面我们来瞧瞧:
菱形继承问题:菱形继承的对象模型探究以及解决菱形继承数据冗余和二义性问题
总结:
1.多重继承比单一继承复杂,他可能导致新的歧义性,以及对virtual继承的需要.
2.virtual继承会增加大小,速度,初始化复杂度等待成本.如果virtual base class不带任何数据,将是最具使用价值的情况.
3.多重继承的确有正当用途。其中一个情节涉及"Public继承某个Interface class"和"private继承某个协助实现的class"的两相结合