欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

阿龙的学习笔记---Effective C++---第六章:继承与面向对象设计

程序员文章站 2022-06-12 20:44:43
...

  • 条款32:确定你的public继承出is-a关系

    • 公开继承public inheritance代表一种is-a关系,D是B的继承,那么B是一种更一般化的概念,而D是一种特殊化的概念。B可以实现的,D一定可以实现,这才是is-a关系。

    • 比如square正方形与rectangle矩形之间的关系,在数学上来说,正方形就是一种矩阵。但是假如在面向对象中表示呢?

      假如rectangle可以随意改变长和宽的函数,square继承之后可以使用吗?答案是否定的。

    • 所以public继承关系的原则就是:适用于base-class的每一个方法一定要适用于derived-class。


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

    • 例如有一个global变量int x,函数内又定义了一个int x,那么在函数内出现x时,编译器会找到函数内定义的这个,这就对global的变量造成了遮掩。

    • 在函数继承中同样会有这个问题,以函数为例(其他也是一样,只讨论名称):

      class Base
      {
           public:
           virtual void mf1();
           virtual void mf1(int);
           virtual void mf2();
           void mf3();
           void mf3(int);
           void mf4();
      };
      
      class Derived: public Base
      {
           public:
           virtual void mf1();
           virtual void mf2();
           void mf3();  
      };
      
      Derived d;
      int x;
      d.mf1();	//调用Drived::mf1();
      d.mf1(x);	//调用失败Base中的mf1(int)被Drived::mf1()函数隐藏。
      d.mf2();	//调用Drived::mf2();
      d.mf3();	//调用Drived::mf3();
      d.mf3(x);	//调用失败Base中的mf3(int)被Drived::mf3()函数隐藏。
      d.mf4();	//调用Base::mf4();
      

      比如这个例子中,mf1、mf2、mf3都在派生类中重新声明,无论是不是virtual函数。重载的函数也并没有继承下来。

    • 如果想使用base中的同名函数,可以使用using声明:

      class Derived: public Base{
      public:
      		//让Base class内名为mf1和mf3的所有东西在Derived作用域内都可见,且为public
              using Base::mf1;
              using Base::mf3;
              virtual void mf1();
              void mf3();
              void mf4();
      };
      
      int x;
      d.mf1();//调用Drived::mf1();
      d.mf1(x);//调用Base中的mf1(int)。
      d.mf2();//调用Drived::mf2();
      d.mf3();//调用Drived::mf3();
      d.mf3(x);//调用Base中的mf3(int)。
      d.mf4();//调用Base::mf4();
      
    • 如果并不想使用base中的所有同名函数,那么可以写一个转交函数(forwarding function)

      class Derived: public Base{
      public:
          virtual void mf1(int x)
          { base::mf1(x); }
      };
      
      Derived d;
      d.mf1(5);	//正确
      d.mf();		//错误
      

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

    • 接口继承和实现继承是说:在派生类中,你希望只继承某个函数的接口声明;还是希望能够继承接口和实现,并且也可以覆写;或者只希望继承接口和声明但不能覆写。 这就有三种做法。

    • 1、pure-virtual纯虚函数的目的就是让派生类只继承函数接口,在所有的派生类中必须要再手动实现,拥有pure-virtual函数也会让base类成为一个抽象类,不能够创建实体,只为了继承。

    • 2、impure-virtual函数让derived-class继承了函数接口以及缺省实现,派生类中可以使用继承而来的实现,也可以重写自己特殊的实现。

      但这种做法也可能会有漏洞,可能会使得派生类忘记重写而造成不好的影响。

      一种做法是将virtual函数接口和缺省函数实现分开,virtual函数接口被声明为pure-virtual函数,而缺省函数实现作为base类中的一个protected非虚函数。如果要使用缺省行为时,则直接调用那个缺省函数实现。

      另一种做法相似,但更简单。由于pure-virtual函数也可以定义,虽然不会被缺省继承,但是可以在派生类中调用。只需要在前面加上base类的名称限定符。

    • 3、non-virtual函数用来继承一个函数接口并接收强制性实现,不可以重写(除非被名称隐藏,类似上一条)