明智而审慎地使用private继承——条款39

        条款32曾经论证过C++如何将public继承视为is-a关系。在那个例子中我们有个继承体系,其中class Student以public形式继承class Person,于是编译器在必要时刻(为了让函数调用成功)将Students暗自转换为Persons。现在我们再重复该例的一部分,并以private继承替换public继承:

class Person { ... };
class Student:private Person { ... };    // 这次改用private继承
void eat(const Person& p);               // 任何人都会吃
void study(const Student& s);            // 只有学生才到校学习

Person p;                       // p是人
Student s;                      // s是学生

eat(p);                         // 没问题,p是人
eat(s);                         // 错误!吓,难道学生不是人?!

        显然private继承并不意味is-a关系。那么它意味什么?

        如果classes之间的继承关系是private,编译器不会自动将一个derived class对象(例如Student)转换为一个base class对象(例如Person)。这和public继承的情况不同。这也就是为什么通过s调用eat会失败的原因。第二条规则是,由private base class继承而来的所有成员,在derived class中都会变成private属性,纵使它们在base class中原本是protected或public属性。

       Private继承意味implemented-in-terms-of(根据某物实现出)。如果你让class D以private形式继承class B,你的用意是为了采用class B内已经备妥的某些特性,不是因为B对象和D对象存在有任何观念上的关系。private继承纯粹只是一种实现技术(这就是为什么继承自一个private base class的每样东西在你的class内都是private:因为它们都只是实现枝节而已)。借用条款34提出的术语,private继承意味只有实现部分被继承,接口部分应略去。如果D以private形式继承B,意思是D对象根据B对象实现而得,再没有其他意涵了。Private继承在软件“设计”层面上没有意义,其意义只及于软件实现层面。

        Private继承意味is-implemented-in-terms-of(根据某物实现出),这个事实有点令人不安,因为条款38才刚指出复合(composition)的意义也是这样。你如何在两者之间取舍?答案很简单:尽可能使用复合,必要时才使用private继承。何时才是必要?主要是当protected成员和/或 virtual函数牵扯进来的时候。

        考虑一个实例,修改Widgets class,让它记录每个成员函数的被调用次数。先找到如下class:

class Timer {
public:
	explicit Timer(int tickFrequency);
	virtual void onTick() const;        // 定时器每滴答一次,此函数就自动调用一次
	...
};

        为了让Widget重新定义Timer内的virtual函数,Widget必须继承自Timer。但public继承在此例并不适当,因为Widget并不是个Timer。是呀,Widget客户总不该能够对着一个Widget调用onTick吧,因为观念上那并不是Widget接口的一部分。如果允许那样的调用动作,很容易造成客户不正确地使用Widget接口,那会违反条款18的忠告:“让接口容易被正确的使用,不易被误用”。在这里,public继承不是个好策略。

        我们必须以private形式继承Timer:

class Widget: private Timer {
private:
	virtual void onTick() const;    // 查看Widget的数据...等等。
	...
};

        这是个好设计,但不值几文钱,因为private继承并非绝对必要。如果我们决定以复合(composition)取而代之,是可以的。只要在Widget内声明一个嵌套式private class,后者以public形式继承Timer并重新定义onTick,然后放一个这种类型的对象于Widget内。下面是这种解法的草样:

class Widget {
private:
	class WidgetTimer: public Timer {
		virtual void onTick() const; 
		...
	};
	WidgetTimer timer;
	...
};

                                                  

        这个设计比只使用private继承要复杂一些些,因为它同时涉及public继承和复合,并导入一个新class(WidgetTimer)。坦白说,我展示它主要是为了提醒你,解决一个设计问题的方法不只一种,而训练自己思考多种做法是值得的(看看条款35)。尽管如此,我可以想出两个理由,为什么你可能愿意(或说应该)选择这样的public继承加复合,而不是选择原先的private继承设计。

        首先,你或许会想设计Widget使它得以拥有derived classes,但同时你可能会想阻止derived classes重新定义onTick。如果Widget继承自Timer,上面的想法就不可能实现,即使是private继承也不可能。(还记得吗,条款35曾说derived classes可以重新定义virtual函数,即使它们不得调用它。)

        第二,你或许会想要将Widget的编译依存性降至最低。如果Widget继承Timer,当Widget被编译时Timer的定义必须可见,所以定义Widget的那个文件恐怕必须#include Timer.h。但如果WidgetTimer移出Widget之外而Widget内含指针指向一个WidgetTimer,Widget可以只带着一个简单的WidgetTimer声明式,不再需要#include任何与Timer有关的东西。对大型系统而言,如此的解耦可能是重要的措施。关于编译依存性最小化,详见条款31

        大多数继承相当于is-a,这是指public继承,不是private继承。复合和private继承都意味is-implemented-in-terms-of,但复合比较容易理解,所以无论什么时候,只要可以,你还是应该选择复合。

请记住

  • Private继承意味is-implemented-in-terms-of(根据某物实现出)。它通常比复合(composition)的级别低。但是当derived class需要访问protected base class的成员,或需要重新定义继承而来的virtual函数时,这么设计是合理的。
  • 和复合(composition)不同,private继承可以造成empty base最优化。这对致力于“对象尺寸最小化”的程序库开发者而言,可能很重要。
在现代程序设计中,虽然许多现代编程语言已经弃用了`goto`语句,但在一些特定的编程环境中,如嵌入式系统或性能敏感的应用中,`goto`语句仍可能被使用。正确使用`goto`语句需要权衡其带来的效率提升和可能对代码清晰度造成的负面影响。以下是几点建议: 参考资源链接:[程序设计语言中的goto语句之争:历史、影响与解决方案](https://wenku.youkuaiyun.com/doc/4piyki46gb) 1. 只在代码清晰可读的情况下使用`goto`,避免使用于复杂的控制流中,以免造成维护困难。 2. 使用`goto`时,尽量在函数或代码块的局部范围内,不要跨越函数边界进行跳转,以免破坏程序的封装性和模块化。 3. 当存在多个返回点或退出点的条件语句时,可考虑使用`goto`来避免代码重复,但前提是这样的跳转能够使代码更加简洁明了。 4. 在错误处理中,如果`goto`能够提高程序的错误恢复效率,例如从多层嵌套循环中快速退出,可以适当使用。 5. 在使用`goto`的同时,应遵循适当的编程标准和最佳践,确保在团队协作中代码的一致性和可维护性。 6. 对于有性能瓶颈的部分代码,如果通过使用`goto`能够明显提升性能,但不会显著降低代码的可读性,可以作为优化手段之一。 7. 考虑到程序的正确性证明,应避免使用`goto`来构建复杂的控制流,而是尽可能使用结构化的编程构造来确保程序逻辑的清晰。 综上所述,`goto`语句的使用应当非常谨慎,并严格限制在那些确能够带来清晰和效率提升的场景中。对于大多数现代编程场景,推荐使用结构化的编程构造来提高代码的可读性和可维护性。更多关于`goto`语句的讨论及其在程序设计中的影响和解决方案,可以参考《程序设计语言中的goto语句之争:历史、影响与解决方案》这份资料,它提供了对`goto`语句深入的分析和全面的视角。 参考资源链接:[程序设计语言中的goto语句之争:历史、影响与解决方案](https://wenku.youkuaiyun.com/doc/4piyki46gb)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值