C++之避免遮掩继承而来的名称(33)---《Effective C++》

1 篇文章 0 订阅
1 篇文章 0 订阅

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

C++中我们需要注意作用域问题,尤其在继承体系中,可能存在覆盖问题,先看一下如下代码,了解一下作用域概念:

int x;
void someFunc()
{
    double x;
    std::cin>>x;
}

其作用域表示为:

这里写图片描述
可以看到当编译器处于someFunc函数的作用域中并遇见x时候,她现在local作用域内查找x,如果找不到在向外层作用域查找,如果还是不行继续向外层拓展,因此内部的x覆盖了外部x的值!

class Base{
private:
    int x;
public:
    virtual void mf1()=0;
    virtual void mf2();
    void mf3;
    ...
};
class Derived:public Base{
public:
    virtual void mf1();
    void mf4();
    ...
};
void Derived::mf4()
{
    ...
    mf2();
    ...
}

作用域:
这里写图片描述
由于Derived classes继承了声明于Base classes内的所有东西,实际运作方式是,derived class作用域被嵌套在base class作用域内,derived class作用域中的值可能会覆盖掉base class作用域中的值!正如上面的代码,编译器遇到mf2()的时候,首先查找其外围作用域,也就是class Derived覆盖的作用域,没有找到,则想外层继续查找,这里指的是class Base作用域,如果还是没有的话则继续向外即查找Base的那个namespace的作用域,如果还是没有找到,最后往global作用域中查找!

问题:继承体系中的作用域遮掩问题

class Base{
private:
    int x;
public: 
    virtual void mf1()=0;
    virtual void mf1(int);
    virtual void mf2();
    void mf3();
    void mf3(double);
};
class Derived:public Base
{
public:
    virtual void mf1();
    void mf3();
    void mf4();
    ...
};

Derived d;
int x;
...
d.mf1();
d.mf1(x);//错误,因为Derived::mf1遮掩了Base::mf1
d.mf2();
d.mf3();
d.mf3(x);//错误,因为Derived::mf3遮掩了Base::mf3

如何解决这个问题呢?
1)通过using声明式可以比较完整的解决该问题,代码如下:

class Base{
public:
    virtual void mf1()=0;
    virtual void mf1(int);
    virtual void mf2();
    void mf3();
    void mf3(double);
    ...
};
class Derived:private Base{
public:
    using Base::mf1;
    using Base::mf3;
    virtual void mf1();
    void mf3();
    void mf4();
    ...
};
Derived d;
int x;
...
d.mf1();
d.mf1(x);//ok,调用Base::mf1
d.mf2();
d.mf3();
d.mf3(x);//ok,调用Base::mf3

这种方式当然没有问题,通过using声明将基类中的相应的函数提升到子类中,保证子类中的不被覆盖,可以看看如下的代码和运行结果进行深入理解:

#include <iostream>
#include <string>
using namespace std;
class Base{
public:
    Base(int x) :x(x){

    }
    void mf1(){
        cout << "Base::mf1()" << endl;
    };
    void mf1(int h){
        cout << "这是Base中的mf1(int h)===>" <<h<< ",带参数哟!" << endl;
    }
    void mf2(){
        cout << "Base::mf2()" << endl;
    };
    void mf3(){
        cout << "Base::mf3()" << endl;
    };
    void mf3(double d){
        cout << "这是Base中的mf3(double d)===>" << d << ",带参数哟!" << endl;
    }

private:
    int x;
};
class Derived :public Base{
public:
    Derived(int x) :Base(x){

    }
    using Base::mf1;
    using Base::mf3;
    void mf1(){
        cout << "Derived::mf1()" << endl;
    }
    void mf3(){
        cout << "Derived::mf3()" << endl;
    }
    void mf4(){
        cout << "Derived::mf4()" << endl;
    }
};

int main(){
    Derived d(10);
    d.mf1();
    d.mf3(100.00);
    d.mf1(10);
    return 0;
}

运行结果:
这里写图片描述
这种方式意味着如果你继承了Base class并加上重载函数,而你又希望重新定义或者覆写其中一部分,那么你必须为那些原本被这样的每个名称引入一个using声明式,否则某些你希望继承的名称会被遮掩。

2)如果有时候你嫌弃Base class中的所有函数都继承的话显得相当累赘,我们并不想继承所有的函数,这个时候应该怎么解决呢?在public继承中这种根本不可能出现,因为public继承满足is-a条例,此时我们可以通过private继承机制完成,using声明在这儿就没法使用了,因为using声明会令继承而来的某给定名称之所有的同名函数在derived class中都可见!假设现在Derived class指向继承Base class中的void mf1()函数,并不想继承带参数的麻烦,不能使用public继承吧,同时不能使用using声明吧,因为这样会使得继承而来的某给定名称之所有的同名函数在derived class中都可见,所以,我们通过转交函数实现:

class Base{
public:
    virtual void mf1()=0;
    virtual void mf1(int);
    ...
};
class Derived:private Base{
public:
    virtual void mf1(){    //转交函数(forwarding function)
        Base::mf1();//暗自成为inline函数
    }
    ...
};
Derived d;
int x;
d.mf1();//ok
d.mf1(x);//Error,Base::mf1(int)被遮掩了

inline转交函数的另一个用途是为那些不支持using声明式的老编译器开辟一条新路,将继承而得的名称汇入derived class作用域内。

总结:

  1. derived class内的名称会遮掩base class中的名称,在public继承下从来没有人希望如此;
  2. 为了让这样的名称重见天日,可使用using声明式或者转交函数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值