云枫的菜园子

带着一个流浪的心,慢慢沉淀。 https://github.com/CloudFeng

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理
  15 Posts :: 0 Stories :: 6 Comments :: 0 Trackbacks

    今天看了《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。这里所写都是自己的读书的时候梳理做的笔记罢了。希望对他人有用,那是我的荣幸。

posted on 2015-03-24 21:17 CloudFeng 阅读(...) 评论(...) 编辑 收藏