“Effective C++ Third Edition”学习笔记(四)

冬日暖阳,

隔着玻璃窗,

warm着body和思绪,

时光在音乐间流畅,


赏心悦目,

随着scott的文字,

徜徉在C++知识的海洋中,


激扬澎湃,

音乐和思绪,

温故知新之时,

随手记下要点,

以备来日再读。





继承与面向对象编程

   您是否了解C++标准中某些特性的真正含义那? 同样一个特性,不同人的理解是不同的。就像一张白纸,可以书写优美的诗句,也可以乱涂乱画。 正确地理解C++某些特性的本质含义,引入时的初衷,可以让我们正确使用他们。 不正确的使用可能会带来异常,甚至是灾难。
   大多数程序员关注如何正确地遵守规则,而黑客们关注规则的漏洞,关注如何错误地使用它们并引起异常。

    1)“public继承”意味着“is-a”,如果你尝试让它带着其他含义,你会惹祸上身。
    2)“private继承”意味着“is-implemented-in-terms-of”,也就是说使用base类中定义的方法,功能组件等来实现derived类的对外公共接口,derived 类并不继承base的对外接口,因此它们之间不是is-a关系。
    3)“composition聚合”能实现private同样的功能,但是却不会涉及到多重继承的问题,因此通常来说比private继承更建议被使用。
    4)“virtual函数”意味着“接口必须被继承”;
    5)“non-virtual函数”意味着“接口和实现都必须被继承”;



条款 36: Never redefine an inherited non-virtual function.


     由于按照C++语言设计者的设想:“non-virtual函数”意味着“接口和实现都必须被继承”。所以在类继承体系中,non-virtual是不应该被重新定义的。另外non-virtual不同于virtual,它是静态绑定的,也就是说调用对象的静态类型决定了该调用哪个non-virtual函数,不像virtual函数可以实现多态。


条款 37: Never redefine a function's inherited default parameter value.

1)前面我们已经讨论过:你不应该重新定义基类的non-virtual函数。所以这里我们只讨论virtual函数。
2)我们知道在C++的多态中,virtual函数是动态绑定的,但是virtual函数的默认参数确是“静态绑定(statically bound)”。 正是因为一个是动态绑定,一个是静态绑定,所以造成我们在实现多态机制的类层次中不能重新定义继承来的函数的默认参数值。 否则会造成动态绑定的是derived class的virtual函数,但是该函数使用的默认参数确实base class类的默认参数。
3)解决办法是,将有默认形参的那个函数在基类中定义为non-virtual(这样继承类就不能修改它,完全继承它),将做实际工作的函数定义为virtual,并设置为private权限。如下所示:

   class Shape{
    public:
       enum ShapeColor{Red, Green,Blue};
       void draw(ShapeColor color = Red) const
      {
         doDraw(color);
      }
      ...

     private:
      virtual void doDraw(ShapeColor color) const = 0;
   };

  class Rectangle: public Shape{
    public:
     ...
   
   private:
     virtual void doDraw(ShapeColor color) const;
  };


条款 38: Model "has-a" or "is-implemented-in-terms-of" through composition.

   1)composition表示一个对象包含有另外一个对象,由不同的对象组成。属于has-a关系。例如人有一个名字,而不能说人是一个名字;人有一个地址,而不能说人是一个地址。
   2)通过在类定义中包含另外一个类对象成员变量,来实现has-a这种关系。
   3)“composition复合”能实现private同样的功能,但是却不会涉及到多重继承的问题,因此通常来说比private继承更建议被使用。
   4)在设计模式中,有一条建议“多用聚合,少用组合”。聚合和组合的区别在于:聚合关系是“has-a”关系,组合关系是“contains-a”关系;聚合关系表示整体与部分的关系比较弱,而组合比较强;聚合关系中代表部分事物的对象与代表聚合事物的对象的生存期无关,一旦删除了聚合对象不一定就删除了代表部分事物的对象。组合中一旦删除了组合对象,同时也就删除了代表部分事物的对象。“聚合”使用对象指针,而“组合”使用对象。关于它们两者的优缺点,可以考虑两个问题:a)类的尺寸问题。b)类之间的编译依赖性问题。


条款 39: Use pritvate inheritance judiciously.


    1)如果类之间的继承关系是private,编译器不会自动将一个derived类对象转换为一个base类对象,而这是public类继承所常见的一种特性;
   2)如果类之间的继承关系是private,由private base类继承来的所有成员,在derived类中都是private的,不管它们原来在base类中是public还是protected。
   3)如果你让class D以private继承class B,你的用意是为了采用class B中已经备妥的某些特性,不是因为B对象和D对象之间存在任何观念上的关系。你要使用class B中的方法和特性,来实现自己的对外接口等。private继承纯粹是一种实现技术。
   4)尽可能多用复合(composition),必要时才使用private继承。何时必要?主要是党protected成员或者virtual函数牵扯进来时。
   5)同一个问题使用复合 和private的两种解法。
    
     a)首先使用private继承
      //一个定时器类
      class Timer{
       public:
         explicit Timer(int tickFrequency);//设置时钟滴答频率
         virtual void onTick() const; //每次滴答时调用的处理函数。
         ...   
     };
  
     //一个widget需要一个Timer,因为两个类之间不存在is-a关系,所以使用private继承
     class Widget: private Timer{
      private:
         virtual void onTick() const;//它应该是private的,因为是private继承。
     };

     b)方法2使用public继承加复合(composition)
     
     class Widget{
      private:
         class WidgetTimer: public Timer{
         public:
            virtual void onTick() const;
            ....
         };
       WidgetTimer timer;
       ...
     };  

   
条款 40: Use multiple inheritance judiciously。

   1)在继承路线图中,当某个derived类和某个base类之间有一条以上的连通路线时,多重继承存在。
   2)多重继承时,如果不同继承路线上的祖先类中具有同名函数存在的话,则可能会存在调用歧义。到底该调用哪个祖先中的函数那?
   3)通过virtual继承,解决多重继承时,某个derived类对象从某个base类中,通过不同的继承路线,继承来多份同一个成员。
   4)使用virtual继承是需要付出运行和性能代价的。为了避免继承得来的成员变量重复,编译器必须提供若干幕后戏法,而其后果是:使用virtual的哪些类所产生的对象往往比使用non-virtual继承的兄弟们体积大,访问virtual base class的成员变量时,也比访问non-virtual base class的成员变量速度慢。
   5)virtual base的初始化任务是由继承体系中最低层类来负责的,这暗示 derived类如果派生自virtual base class而需要初始化,必须知道其virtual base,不论它距离virtual base类多么遥远。当一个新的derived class加入到继承体系时,它必须承担其virtual base的初始化责任,直接或者间接。(如果它的直接父类的构造函数中已经初始化了virtual base类,算不算间接初始化???)
   6) 非必要时不要使用virtual base。平时请使用non-virtual继承。如果你必须使用virtual base class,则请在virtual base class中避免放置数据。因为没有数据变量,所以你就不需要初始化它。
   7)多重继承只是面向对象工具箱里的一个工具而已。和单一继承比较,它通常比较复杂,使用上也比较难以理解,如果你有个单一继承的设计方案,而它大约等价于一个多重继承设计方案,那么单一继承设计方案技术一定比较受欢迎。然后有些时候,多重继承的确是完成任务最简洁,最易维护,最合理的做法,那么请勇敢使用它。只要你确定自己是在明智而审慎的情况下使用它。

 

 

 

你可能感兴趣的:(“Effective C++ Third Edition”学习笔记(四))