《Effective C++第三版》读书笔记――设计与面向对象设计

6 设计与面向对象设计
~~~~~~~~~~~~~~~~~~~~~

6.1 确定public继承表达的是is-a关系
===================================
   1. 所谓is-a的关系不是指语言中的关系(比如鲸鱼是鱼,正方形是一种长方形等等),它要求能够施行于base class对象身上的每件事情,都可以施行于derived class对象身上.
   2. 注意区分is-a(是一个),has-a(有一个),is-implemented-in-terms-of(根据某物实现出)

6.2 在子类中小心定义与父类相同名称的方法
=========================================
   1. 子类中的同名方法会覆盖父类中的 *所有* 同名方法.即使父类和子类中的汗水有不同的参数类型也适用,不论函数是virtual或non-virtual方法都适用.
   2. 可以在子类中用using 父类::同名方法的方式,使父类方法在子类中可见.而继承机制一如既往地运行

6.3 区分接口继承和实现继承
===========================
   1. pure virtual函数也可以提供定义,但调用它的唯一方法是"调用时明确指出class名称":pt->class::pure_virtual_func()
   2. 声明一个pure virtual函数的目的是为了让derived classes只继承函数接口
   3. 声明一个impure virtual函数的目的,是为了让derived class继承该函数的接口和缺省实现
      可以通过将接口声明为一个pure virtual函数,将实现抽取作为一个non-virtual函数的方法来防止子类忘了重载impure-virtual的情况出现.但这种方法容易因过度雷同的函数名称而引起class命名空间污染问题.
      我们也可以将接口声明为pure virtual函数,但同时也为这个pure-virtual提供缺省实现的方法来分离接口和缺省实现.
      本质上,上面两种方法是为了将impure-virtual函数分割成两部分:声明部分表现的是接口(子类必须有),定义部分表现出缺省行为(子类可能有,但是需要在子类中明确调用),可以让着两个函数享有不同的保护权限.
   4. 实现non-virtual函数的目的是为了令derived class继承函数的接口及一份强制性实现.

6.4 考虑virtual函数以外的其他选择
==================================

6.4.1 NVI模式(non-virtual interface)
-------------------------------------
    1. 令客户通过public non-virtual成员函数间接调用private virtual函数,称为NVI手法.
    2. NVI手法允许derived class重新定义virtual函数,从而赋予它们"如何实现机制"的控制能力,但base class保留诉说"函数何时被调用"的权利.
    3. NVI手法没必要一定让virtual是private,也可以是protected,但是不能为public

6.4.2 借由Function Pointers实现Strategy模式
--------------------------------------------
    1. 通过在类中包含指向功能的指针来让类具有指定的功能
    2. 这种方法可以实现
       * 同一个类对象可以有不同的实现功能
       * 实现功能可以在运行期变更
       * 这种方法要求功能实现不依赖类的内部成分,因为这个方法已经不是类的成员函数了.

6.4.3 古典的Strategy模式
-------------------------
    1. 传统的Strategy做法会将功能函数做成一个分离的类继承体系中的virtual成员函数.      

6.5 绝不重新定义public继承而来的non-virtual函数
================================================
   1. 若子类和父类有同名的non-virtual方法,那么一个指针调用哪个方法,就看指针声明类型是子类还是父类.

6.6 绝不重新定义继承而来的缺省参数值
=====================================
   1. virtual函数是动态绑定的,而缺省参数值却是静态绑定的.
   2. 指针的静态类型指声明该指针时的类型
      指针的动态类型指指针所指对象的实际类型
   3. 由于virtual函数是动态绑定,而却是函数是静态绑定的,所以当一个指向base饿指针在调用一个定义于derived class内的virtual函数时,使用的确实base class为它指定的缺省函数值!!
 

  
  
  
  
  1. struct B 
  2.   { 
  3.   virtual void say(string word = "b"){cout<<word<<endl;} 
  4.   }; 
  5.   struct D:public B 
  6.   { 
  7.   virtual void say(string word = "d"){cout<<"i am "<<word<<endl;} 
  8.   }; 
  9.   int main() 
  10.   { 
  11.   B* pb = new D(); 
  12.   pb->say();       //i am b; 
  13.   return 0; 
  14.   } 



   4. 既然base中缺省参数和derived中的缺省参数必须一致,那么在derived中声明同样的缺省函数就显得代码重复,而且带着相依性了.
      可以使用NVI的手法,让non-virtual函数指定缺省参数,而private virtual函数负责真正的工作.

6.7 明智而审慎地使用private继承
================================
   1. 如果class之间的继承关系是private,编译器不会自动将一个derived class对象转换为base class对象.
      由private base class 继承来的变量全部变为private的
   2. private继承意味着implemented-in-terms-of(根据某物实现出).
      如果你让class D以private形式继承class B,那么用意应该为采用class B内已经具备的某些特性,而不是因为B对象和D对象存在任何观念上的关系. private继承意味着只有实现部分被继承,而接口部分被略去
   3. 复合的意义也是is-implemented-in-terms-of,而且复合比较容易理解,因此尽可能使用复合,必要时才使用private继承.
   4. 当面对并不存在is-a关系的两个classes,其中一个需要访问另一个protected成员,或需要重新定义其一或多个virtual函数,则可以使用private继承.

6.8 明智而审慎地使用多重继承
=============================
   1. 调用多个父类中同名函数的问题:
      多个父类中的同名函数的调用是歧义的,即时两个函数中只有一个是可取的(即一个是public,一个是private).因为C++解析重载函数调用的规则是,先找出匹配函数,再检查其可取性.
      为了解决这个歧义,必须明确指明调用哪一个base class中的函数,例如sub.base1::func()
   2. 钻石型多重继承
      所谓钻石型多重继承指多个父类继承于同一个base class.这是带来的问题是,这个base class中的成员变量,是否应该重复
      C++缺省的做法是重复的,如果不想要重复,那么父类在继承base class时需声明为virtual继承,即class D:virtual public B
   3. virtual继承
      由于virtual继承的开销太大,因此
      * 尽量不要使用virtual继承,多使用non-virtual继承
      * 如果必须使用virtual继承,尽量避免在virtual-base-class中放置数据
 

你可能感兴趣的:(C++,面向对象)