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

来源:互联网 发布:php数组相同键值相加 编辑:程序博客网 时间:2024/05/18 14:12

Item 32:确定你的public继承塑膜出is-a的关系
如果你令class D以public继承class B,你便是告诉编译器说,每一个类型为D的对象同时也是一种B对象,反之如果你需要一个D对象,B对象无法效劳,因为虽然每个D对象都是一个B对象,反之不成立。
**总结:**public继承意味着is-a,适用于base classes身上的每一件事也适用于derived classes身上,因为没一个derived class都是一个base class对象。


Item 33:避免遮掩继承而来的名称
总结:①derived class内的名称会遮掩base class内的名称,在public继承下从来没有人希望如此。
②为了让被遮掩的名称在见天日,可使用using声明式或者转义函数。


Item 34:区分接口继承和实现继承
总结:①接口继承和实现继承不同,在public继承之下,derived class总是继承base class的接口。
②pure virtual函数只具体指定接口继承。
③impure virutal函数具体指定接口继承及缺省实现继承。
④non-virtual函数具体指定接口继承以及强制性实现继承。


Item 35:考虑virtual函数以外的其他选择
藉由non-virtual interface首发实现template method模式。
这个流派主张virtual函数应该几乎总是private,这个流派的拥护者建议,较好的设计是保留healthvalue为public成员函数,但让它成为non-virtual,并调用一个private virtual函数进行实际工作。

class GameCharacter{public:    int healthValue() const    {    int retval=dohealthValue();//做一些真正的工作    return retval;    }private:    virtual int dohealthValue()const//派生类可重新定义它    {    ...//缺省算法,计算健康函数    }}

这一基本设计,也就是令客户通过public non-virtual成员函数间接调用private virtual函数,称为non-virtual interface(NVI)手法。它是所谓template method设计模式(与C++templates 并无关系)的一个独特表现形式,我把这个non-virtual函数称为virtual函数的外覆器(wrapper)。
优点:可以确保外覆器在一个virtual函数被调用之前设定好场景,并且在调用之后清理场景。“事前工作做”可包含锁定互斥器。制造运转日志及录像,验证class约束条件等,“事后工作”可以包括互斥器,解除锁定,验证函数的事后条件,再次验证class约束条件等等,如果让客户直接调用virtual函数,则没有这个优点。

function pointer实现strategy模式class GameCharacter;//前置声明int defaultHealthcalc(cosnt GameCharacter&gc);class GameCharacter{public:    typedef int (*HealthcalcFunc)(const GameCharacter&);    explict GameCharacter(HealthcalcFunc hcf=defaultHealthCalc):healthFUnc(hcf){}    int healthValue()const    { return HealthFunc(*this);}private:    HealthCalcFunc healthFunc;    }}

这个做法是常见的strategy设计模式的简单应用,拿它和“基于GameCharacter”继承体系内之virtual函数比较,它提供了某些有趣的弹性:

  1. 同意任务类型的不同实体可以有不同的健康计算函数。
  2. 某已知任务的健康指数计算函数可以在运行期变更。

    总结:①virtual函数的替代方案包括NVI手法和Strategy设计模式的多种形式,NVI手法是一个特殊形式的template method设计模式。
    ②将机能从成员函数移到class外部函数,带来的一个缺点是,非成员函数无法访问class的non-public成员。


Item36:绝不重定义继承来的non-virtual函数
item32说过所谓的public继承意味着is-a的关系,item34则指出为什么在class内声明一个non-virtual函数会为该class建立一个不变性,凌驾于特异性,如果你将这两个观点施行于两个classB和D以及non-virtual成员函数B:mf身上,那么:

  1. 适用于B对象的每一件事,也适用于D对象,因为每一个D对象都是一个B对象。
  2. B的derived class一定会继承mf的接口和实现,因为mf是B的一个non-virtual函数。
    现在,如果D重新定义mf,你的设计便出现矛盾,如果D真的有必要实现出与B不同的mf,并且如果没一个D对象-不管有多么特化-真的必须使用D所提供的mf实现代码,那么“每一个D都是一个B”就不是真,既然如此,D就不该以public形式继承B。另一方面,如果D真的必须以public形式继承B,并且如果D真的需要实现出与B不同的mf,那么mf就无法为B反应出“不变性凌驾于特异性”的性质,既然这样mf应该什么为virtual函数,最后,如果每个D真的是一个B,并且如果mf真的为B反映出“不变性凌驾于特异性”的性质,那么D就不需要重新定义mf,而且它也不应该尝试这么做,不论是哪一种观点:任何情况下都不应该重新定义一个继承而来的non-virtual函数。

Item38:通过符合塑模出has-a或者“根据某物实现出”


Item39:明智而审慎地使用private继承
总结:①private继承意味着is-implementated-in-terms-of(根据某物实现出),它通常比private继承1 复合的级别低,但是当derived class需要访问protected base class的成员,或者重新定义继承而来的virtual函数时候,这么设计是合理的。
②和复合不同,private继承可以造成empty base class最优化,这对于致力于“对象尺寸最小化”的程序库开发者来说,可能很重要。


Item40:明智而审慎地使用多重继承
总结:①多重继承比单一继承更复杂,它可能导致新的歧义性,以及对virtual继承的需要。
②virtual继承会增加大小,速度,初始化复杂度等成本。如果virtual base classes不带任何数据,将是最具有使用价值的情况。
③多重继承的确有正常用途,其中一个情节涉及“public 继承某个interface class”和“private 继承自某个协助实现的class”的俩相组合。

0 0
原创粉丝点击