Effective C++ — 继承与面向对象设计

Effective C++

___________________________________________________




面向对象编程几乎已经风靡两个时代了,所以关于继承,派生,virtual函数等等.但是C++的oop有可能和你原本习惯的oop稍 有不同:

承可以单一继承 或者多继承,每一个继承连接可以是public,protected或private,也可以是virtual或者 non-virtual.然后是成员 函数

各个选项:virtual? non- virtual?pure virtual?以及成员函数和其他语言特性的交互影响: 缺省参数值与virtual函数有什么 交互影响?

继承 如何影响C++的名称查找规则?设 计选型有哪些?如果class的行为需要修改 ,virtual函数是最佳选择吗?接下来就 见证奇迹的时刻了.



条款32:确定你的Public继承塑模出is-a关系



public继承和is-a之间的等价关系听起来颇为简单,但有时候你的直觉可能会误导你.举个例子,大家都知道企鹅是一种鸟, 这是事 .

鸟也可以飞这也 是事实.如果我们天真的以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函数
};

即便如此,此刻我们仍然未能完全处理好这些鸟事,世界并不存在一个"适用于所有软件"的完美设计.所谓最佳设计,取决于系统希

做什么事情.这里 还有一种思想. 别处理我所谓"所有鸟都会飞,企鹅是鸟,但企鹅不会飞"就是为企鹅重新定义fly函数, 令他产 生一个

运行期错误.


void error(const string& msg); //定义于某处

class Penguin :public Bird
{
public:
	virtual void fly()
	{
		error("Attempt to make a penguin fly!");
	}
	.....
};

很重要的是,你必须认知这里所说的某些东西可能和你所想的不同.这里并不是说"企鹅不会飞",而是说"企鹅会飞,但尝试那么 做是

错误的.

总结:

public继承意味着"is-a"关系.适用于base class身上的每一件事情一定也适用于derived classes身上

,因为每个 derived classes 也都是一个base classes对象.


条款33:避免遮掩继承而来的名称



总结:

derived classes内的名称会遮掩base classes内的名称. 在public继承下从来没有人希望

如此 为了让被遮掩的名称重见天日,可使用using生命式或转交函数.



条款34:区分接口继承和实现继承



表面上直接了当的public继承概念,经过更严密的检查之后,发现它由两部分组成:函数接口继承和函数实现继承.这两种继承 的差异,

很像函数声明和 函授定义之间的差异. 身为class的设计者,有时候你会希望derived classes只继承成员函数的接口;有时候你又会希

望derived classes同时继承函 数的接口和实现,但又希望能够覆写他们所继承的实现;又有时候你希望derived classes同时继承函数

的接口和实现,并且不 允许覆写任何东西. pure virtual函数有两个最突出的特性:他们必须被任何"继承了他们"的具备class重新声明

,而且他们在抽象class中通常没有 定义.把这两个性质摆在一起,你就会明白.


声明一个pure virtual函数的目的是为了让derived classes只继承函数接口.

声明简朴的impure virtual函数的目的,是让derived classes继承该函数的接口和却省实现.

声明non-virtual函数的目的是为了令derived classes继承函数的接口及一份强制性实现.


总结:

1.接口继承和实现继承不同.在public继承下,derived classes总是继承base class的接口.

2.pure virtual函数只具体制定接口继承

3.简朴的impure virtual函数具体指定接口继承及却省实现继承.

4.non-virtual函数具体指定接口继承以及强制性实现继承.


 
条款35:考虑virtual函数之外的其他选择



总结:

1.virtual函数的替代方案包括 NVI方法 及 Strategy设计模式的多种形式。NVI方法 自身是一个特殊形式的 Template Method 设
计模式。

2.将机能从成员函数移到class外部函数,带来的一个缺点是,非成员函数无法访问class内的 non-public成员。

3.tr1::function 对象的行为就像一般的函数指针。这样的对象可接纳“与给定的目标签名式兼容”的所有可调用物。


条款36:绝不重新定义继承而来的non-virtual函数

假设我告诉你,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-

B,通过pB调用的non-virtual函数永远是B所定义的版本,即使pB指向一个类型为"b派生之class的对象",一如本例


但另一方面,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最优化. 这对致力于对象尺寸最小化""的程序库开发者而言,可能很重要.


条款40:明智而审慎地使用多重继承


一旦涉及多重继承,C++社群便分为两个基本阵营.其中之一认为如果单一继承是好的,多重继承一定是更好.另一阵营则主张,单

一继承是好的多重继承不值得拥有.他们两个阵营都是会有自己支撑的理由,下面我们来瞧瞧:


菱形继承问题:菱形继承的对象模型探究以及解决菱形继承数据冗余和二义性问题


总结:


1.多重继承比单一继承复杂,他可能导致新的歧义性,以及对virtual继承的需要.

2.virtual继承会增加大小,速度,初始化复杂度等待成本.如果virtual base class不带任何数据,将是最具使用价值的情况.

3.多重继承的确有正当用途。其中一个情节涉及"Public继承某个Interface class"和"private继承某个协助实现的class"的两相结合



1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看READme.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值