现在的位置: 首页 > 综合 > 正文

避免遮掩继承而来的名称

2013年05月17日 ⁄ 综合 ⁄ 共 3107字 ⁄ 字号 评论关闭

相信大家对变量作用域的"名称遮掩"现象已经很熟悉了,比如下面这段代码:

    int x; //global variable
    void someFunc()
    {
        double x;    //local variable
        std::cin>>x; //read a new value to local x
    }

 

 这时当编译器处于someFunc的作用域内并遭遇到名称x时,它先在本作用域中查找具有x名称的变量,如果找不到再到其他作用域找.但这里的两个作用域中的具有相同名称x的变量具有不同的数据类型(someFun中的为double而global中的为int),这不是需要在意的问题.C++的名称遮掩规则只关注名称的遮掩,而名称是否具有相同的类型并不重要.下面我们来重点看一下类中的名称遮掩问题.
class Base
    {
    public:
        virtual void mf1() = 0;
        virtual void mf2();
        void mf3();
        ...
    private:
        int x;
    };
    class Derived:public Base
    {
    public:
        virtual void mf1();
        void mf4()
        {   //一个可能的实现
            ...
            mf2();
            ...
        }
        ...
    };
就像独立的普通函数一样,类中的函数也具有作用域的问题,先看下面这段代码: 这段代码所构造的类作用域大体上可以用下面图示表示:

当编译器执行在mf4中遇到mf2名称的时候,编译器的做法是查找各作用域,看看有没有某个名为mf2的声明式。首先查找local作用域(也就是mf4覆盖的作用域),在那儿没找到任何东西名为mf2,。于是查找其外围作用域,也就是class Derived 覆盖的作用域,还是没找到任何东西名为mf2,于是再往外围移动,本例为base class.在那儿编译器找到一个名为mf2的东西了,于是停止查找。如果Base内还是没有mf2,查找动作便继续下去,首先找内含Base的那个namespace(s)的作用域(如果有的话),最好往global作用域找去。
class Base
    {
    public:
        virtual void mf1() = 0;
        virtual void mf1(int);
        virtual void mf2();
        void mf3();
        void mf3(double);
        ...
    private:
        int x;
    };
    class Derived:public Base
    {
    public:
        virtual void mf1();
        void mf3();
        void mf4();
        ...
    };

现在我们在类中添加一下重载函数,看"名称遮掩"会怎么发生,新代码如下: 这时候作用域的大概图示可以描述如下:

我们现在写一段测试代码去test!

    Derived d;
    int x;
    ...
    d.mf1(); //no problem. call Derived::mf1
    d.mf1(x); //error. Derived::mf1 hiden Base::mf1
    d.mf2();  //no problem. call Base::mf2
    d.mf3();  //no problem. call Derived::mf3
    d.mf3(x); //error. Derived::mf3 hiden Base::mf3

这种结果是不是很出乎你的意料,shit! base class内的mf1和mf3的函数都被derived class内的相应
的名字函数遮掩掉了,Base::mf1与Base::mf3竟然不再被子类继承,偶滴神啊,这种愚蠢的事情怎么能发生,
    class Base{...}; //同上
    class Derived:public Base{
    public:
        //让Base class内名为mf1与mf3的所有东西在
         //Derived作用域内都可见(并且都是public)
        using Base::mf1;
        using Base::mf3;
        ....//同上       
    };

快快阻止它吧!这问题不大,我们可以用using声明式达成目标,我们再来看代码: 重新test上面那段代码: 

    Derived d;
    int x;
    ...
    d.mf1(); //no problem. call Derived::mf1
    d.mf1(x); //no problem. Base::mf1
    d.mf2();  //no problem. call Base::mf2
    d.mf3();  //no problem. call Derived::mf3
    d.mf3(x); //no problem. Base::mf3 

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

而有的时候你不想继承base classes的所有函数.在public继承中这显然不能发生,这违反了"base与
derived classes之间的is-a关系.",然而在private继承下它却可能是有意义的(条款39我们会详述).

例如假设Derived以private形式继承Base,而Derived唯一想继承的mf1是那个无参数版本。using 声明式在这里

派不上用场,因为using 声明式会令继承而来的某给定名称之所有同名函数在derived class中都可见。不,我们

需要不同的技术,即一个简单的转交函数(forwardng functions):

class Base
    {
    public:
        virtual void mf1() = 0;
        virtual void mf1(int);
        ... //same as above
    };
    class Derived:private Base
    {
    public:
        virtual void mf1()      //转交函数(forwarding function)
        {
            Base::mf1();
        }
        ...
    };
    ...
    Derived d;
    int x;
    d.mf1(); //good! call Derived::mf1
    d.mf1(x); //Error as suppose,Base::mf1() was hidden.

inline转交函数(forwarding function)的另一个用途是为那些不支持using声明式(注:这并非正确行为)的老旧编译器另辟一条新路,将继承而得的名称汇入derived class作用域内。
请记住:
◆ derived class内的名称会遮掩base classes内的名称.在public继承下没有人希望如此.
◆ 为了让遮掩的名称再见天日,可使用using声明式或转交函数(forwarding functions).

 

抱歉!评论已关闭.