避免遮掩继承而来的名称(Effective C++_33)

本文探讨了C++中名字遮掩的问题,包括变量和函数的遮掩,特别是基类成员函数被覆盖的情况。通过示例代码解释了如何通过using声明式和转交函数避免遮掩继承而来的名称,以保持继承的正确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、名字覆盖规则

名称的遮掩可以分成变量的遮掩与函数的遮掩两类;

查找规则:一般先查找局部作用域,然后再查找外围作用域;对于含有继承关系的类,先查找局部作用域,再查找派生类覆盖的作用域,然后再查找基类覆盖的作用域,如果都没有找到,最后查找全局作用域;


二、基类成员函数被覆盖
(1)看一段代码:

#include <iostream>
using namespace std;

class Base
{
private:
    int x;
public:
    virtual void mf1() = 0;
    virtual void mf1(int ) { cout << "Base mf1(int )" << endl; }
    virtual void mf2() { cout << " Base mf2()" << endl; }
    void mf3() { cout << "Base mf3()" << endl; }
    void mf3(double ) { cout << "Base mf3(double )" << endl; }
};

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

int main(void)
{
    Derived d;
    int x = 0;
    d.mf1();  // 调用派生类mf1()
    //d.mf1(x); // 错误,同名覆盖
    d.mf2();  // 调用基类mf2()
    d.mf3();  // 调用派生类mf3()
    //d.mf3(x); //错误,同名覆盖,派生类中没有带参数的mf3(int)形式,基类中才有,但是已经被派生类名字覆盖了
    return 0;
}

这段代码中,基类所有名为mf1和mf3的函数都会被派生类的mf1和mf3函数遮掩掉,即使基类中有带参数形式的mf1和mf3也会被屏蔽掉,而且不论是否为虚函数都适用

从上面分析可知,基类中的mf1和mf3重载函数版本形式被屏蔽了,条款32说明,public继承意味着基类和派生类之间的关系是Is-has的关系,即适用于基类的,派生类总适用,那么这里的代码岂不违反了is-has的关系?

因此,需要解决对继承而来的名称造成名字遮掩的行为。

(2)解决方式

class Base
{
private:
    int x;
public:
    virtual void mf1() = 0;
    virtual void mf1(int ) { cout << "Base mf1(int)" << endl; }
    virtual void mf2() { cout << " Base mf2()" << endl; }
    void mf3() { cout << "Base mf3()" << endl; }
    void mf3(double ) { cout << "Base mf3(double)" << endl; }
};

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

int main(void)
{
    Derived d;
    int x = 0;
    d.mf1();  // 调用派生类mf1()
    d.mf1(x); // 正确,调用基类mf1(int)
    d.mf2();  // 调用基类mf2()
    d.mf3();  // 调用派生类mf3()
    d.mf3(x); //正确,调用基类mf3(doubel)
    return 0;
}

这意味着想继承基类以及加上其重载形式,又希望重新定义或者覆写其中一部分,那么你必须为那些原本会被遮掩的每个名称引入一个using声明式

(3)如果你并不想继承Base class的所有函数,在public绝对不可能发生,因为违反了Is-has的关系,那么可以选择采用private。假设Derived 以private形式继承Base,而Derived唯一想要继承的mf1是那个无需参数版本。using 声明式在此派不上用场,因为using声明式会令继承而来的某给定名称之所有同名函数在Derived class中可见。所以我们得采用一个简单的转交函数(forwarding function):

class Base
{
private:
    int x;
public:
    virtual void mf1() = 0;
    virtual void mf1(int ) { cout << "Base mf1(int)" << endl; }
    virtual void mf2() { cout << " Base mf2()" << endl; }
    void mf3() { cout << "Base mf3()" << endl; }
    void mf3(double ) { cout << "Base mf3(double)" << endl; }
};
void Base::mf1(){}
class Derived: private Base
{
public:

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

int main(void)
{
    Derived d;
    int x = 0;
    d.mf1();  // 调用派生类mf1(),实际调用基类的mf1()
    d.mf3();  // 调用派生类mf3()

    return 0;
}

三、总结

1.派生类内的名称会遮掩基类的名称。在public继承下从来没有人希望如此。
2. 为了让被遮掩的名称再见天日,可使用using声明式或转交函数(forwarding functions)。


注:本内容来自《Effective C++ 3rd(侯捷译)》,仅作学习,无任何商业用途

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值