• Effective C++笔记_条款33确定你的public继承塑模出is-a关系


        今天看了《Effective C++》中的条款33,看得十分有趣。明白了以前为啥会出现那种这种问题,原来是C++中名称遮掩规则呀。具体内容见下面文章吧。


      1. derived class 作用域被嵌套在base class作用域内,如下面这个例子

     1      class Base {
     2      private:
     3          int x;
     4      public:
     5          virtual void mf1() = 0;
     6          virtual void mf2();
     7          void mf3();
     8          //...
     9      };
    10 
    11      class Derived: public Base {
    12      public:
    13          virtual void mf1();
    14          void mf4();
    15          //...
    16      };
    17 
    18     void Derived::mf4() 
    19     {
    20         //...
    21         mf2();
    22         //...
    23     }

         此时编译器是咋干活的呢?他的做法是查找各作用域,看看有没有某个名为mf2的声明式。

       首先来看看一下其作用域和继承关系,如下图:

    具体步骤如下
      (1)查找local作用域(此例中局势mf4覆盖的作用域),在那儿没有找到任何名为mf2的东西。
      (2)查找外围作用域,也就是class Derived覆盖的作用域。还是没找到任何东西名为mf2。咋办呢?仍旧不死心呀。看(3)
      (3)再往外围移动,本例为base class。在那儿它找到了一个名为mf2的东西了,于是停止查找。(终于找到了,尼玛太好了,休息咯。)

         如果没有咋办呢?(4)。
       (4)如果Base内还是没有mf2,先找内含Base的那个namespace的作用域(如果有的话),还是木有咋办呢?
       (5)那就往global作用域找去。还是木有呢。不好意思,给你个错误,主人你没定义呀。


    2.关于名称遮掩中在类继承中的更为具体的体现:

     1  class Base {
     2      private:
     3          int x;
     4      public:
     5          virtual void mf1() = 0;
     6          virtual void mf1(int);   // overload
     7          virtual void mf2();
     8          void mf3();
     9          void mf3(double);        // overload
    10          //...
    11      };
    12 
    13      class Derived: public Base {
    14      public:
    15          virtual void mf1();
    16          void mf3();            // redefining
    17          void mf4();
    18          //...
    19      };
    20 
    21      /*
    22         精彩部分来罗:
    23      */
    24     Derived d;
    25     int x;
    26     //...
    27     d.mf1();            // ok, call Derived::mf1
    28     d.mf1(x);            // error, Derived::mf1遮掩了Base::mf1
    29     d.mf2();            // ok, 记得上面那个编译器找东西的规则哟。调用Base::mf2
    30     d.mf3();            // ok, 调用Derived::mf3
    31     d.mf3(x);            // error, Derived::mf3遮掩了Base::mf3

          结合下图就可以知道精彩部分的解释咯。

         

           C++的名称遮掩规则所做的唯一事情就是:遮掩名称。至于名称是否适合相同或不同的类型,并不重要。说白了,我就是看名字,管你啥子类型呀。对于上述例子来说:即使base classes 和derived classes内的函数有不同的参数类型也适用,而且不论函数是virtual或non-virtual 一体适用。


        3.背后的基本理由:为了防止你在程序库或应用框架内建立新的derived class时附带地从疏远的base classes 继承重载函数(注明:重载函数只能是类内,不能再继承中体现。但是这句话理解起来好难,至今没能理解透:()。如果我想用基类的同名函数,咋办呢?使用using声明式,示例如下:

     1 class Base {
     2      private:
     3          int x;
     4      public:
     5          virtual void mf1() = 0;
     6          virtual void mf1(int);   // overload
     7          virtual void mf2();
     8          void mf3();
     9          void mf3(double);        // overload
    10          //...
    11      };
    12 
    13      class Derived: public Base {
    14      public:
    15          /*让Base class 内名为mf1和mf3的所有东西在Derived作用域内都可见(并且public)*/
    16          using Base::mf1;
    17          using Base::mf3;
    18          virtual void mf1();
    19          void mf3();            // redefining
    20          void mf4();
    21          //...
    22      };
    23 
    24      /*
    25         精彩部分来罗:
    26      */
    27     Derived d;
    28     int x;
    29     //...
    30     d.mf1();            // ok, call Derived::mf1
    31     d.mf1(x);            // ok, 调用Base::mf1
    32     d.mf2();            // ok, 记得上面那个编译器找东西的规则哟。调用Base::mf2
    33     d.mf3();            // ok, 调用Derived::mf3
    34     d.mf3(x);            // ok, 调用Base::mf3

          看看使用using之后的作用域图是咋样的,如下图:

       

           这意味如果你继承base class并加上重载函数,而你又希望重新定义或覆写(推翻)其中一部分,那么你必须为那些原本会被遮掩的每个名称引入一个using声明式,否则某些你希望继承的名称会被遮掩。


       4.若想继承base classes 的部分函数,那肯定不能用public继承。如果Derived 以private形式继承Base,而Derived唯一想继承的mf1是那个无参数版本。若使用using声明式,则会继承而来的没给定名称的所有同名函数在derived class中都可见。为了解决这个问题,可以使用一个简单的转交函数。具体代码如下:

     1 class Base {
     2      private:
     3          int x;
     4      public:
     5          virtual void mf1() = 0;
     6          virtual void mf1(int);   // overload
     7          virtual void mf2();
     8          void mf3();
     9          void mf3(double);        // overload
    10          //...
    11      };
    12 
    13      class Derived: private Base {   /*注意这里的继承性是private哟*/
    14      public:
    15          virtual void mf1()
    16          {
    17              Base::mf1();            // 转交函数,可以这么理解,就是交给别人处理啦。
    18          }
    19          void mf3();            // redefining
    20          void mf4();
    21          //...
    22      };
    23 
    24      /*
    25         精彩部分来罗:
    26      */
    27     Derived d;
    28     int x;
    29     //...
    30     d.mf1();            // ok, call Derived::mf1
    31     d.mf1(x);            // error, Base::mf1被遮掩了。

    5.总结:
      derived classes 内的名称会遮掩base classes 内的名称。在public继承下从来没有人希望如此。


      为了让被遮掩的名称再见天日,可使用using申明式或转交函数。


    声明:全文文字都是来源《Effective C++》 3th。这里所写都是自己的读书的时候梳理做的笔记罢了。希望对他人有用,那是我的荣幸。

  • 相关阅读:
    centos下升级python
    微信公众号(二)
    微信公众号的开发(一)
    nginx 新手入门
    Mysql is not allowed to connect mysql server
    docker 新手入门 (阿里镜像仓库的使用)
    docker 新手入门 (web项目的部署)
    jquery easyui combox不能编辑只能选择
    $.messager.confirm 用法
    html下载excel模板
  • 原文地址:https://www.cnblogs.com/cloudfeng/p/4364017.html
Copyright © 2020-2023  润新知