Item 33: Avoid hiding inherited names.

其实本文的话题和继承完全没有关系,隐藏名称是作用域的问题。 在C++中每一对{ }都会开启一个新的作用域,并嵌套在当前作用域中。

一个示例

int x;
void func(){
    double x;
    cin>>x;     // read a new value for local x
}

可以看到double x隐藏了int x,因为C++的名称隐藏规则(name-hiding rules)隐藏的是名称,和类型无关!

继承作用域

子类可以访问父类中的名称,是因为子类的作用域是嵌套(nested in)在父类的作用域中的。 这一点也很符合直觉:

class Base{
public:
    void func_base();
};
class Derived{
public:
    void func_derived(){
        func_base();
    }
};

func_derived()中调用func_base()时,编译器首先检查当前作用域内是否有名称func_base(当然C++是不允许在函数里定义函数的), 没有找到;然后去父作用域Derived中寻找名称func_base,仍然未找到;然后去再上一级作用域Base中寻找func_base,找到了!然后调用Base::func_base()

如果还没找到,编译器还会去Derived所在命名空间下、全局作用域下寻找。

隐藏父类的名称

子类中重写(override)与父类方法同名的方法,将会隐藏父类中所有同名的重载方法。例如:

class Base{
public:
    virtual void func()=0;
    void func(int);
};
class Derived: public Base{
public:
    virtual void func();
};
...
Derived d;
d.func(1);      // Error!

Derived中声明的func方法,隐藏了父类中所有的func名称,包括所有的重载函数。

继承所有重载方法

当你从父类继承来了一系列的重载(overload)方法,而只想重写(override)其中的一个时,可以用using,否则其他重载方法会被隐藏。

class Derived: public Base{
public:
    using Base::func;
    virtual void func();
};
...
d.func(1);      // OK

继承一个重载方法

在public继承中,子类和父类是"is-a"的关系(见Item 32),所以通常我们希望从父类继承所有的方法。 但如果是private继承(见Item 39), 可能你只想要其中的一个,这时可以定义一个转发函数(forwarding function):

class Base{
public:
    virtual void mf1() = 0;
    virtual void mf1(int);
};
class Derived: private Base{
public:
    virtual void f1(){
        Base::mf1();        // 这是一个inline函数,见 Item30
    }
};

总结

  • 子类中的名称会隐藏父类中所有同名的属性。public继承表示这"is-a"的关系,应该避免这样做。
  • 使用using声明或者转发函数可以使父类名称再次可见。

本文采用 知识共享署名 4.0 国际许可协议(CC-BY 4.0)进行许可,转载注明来源即可: https://harttle.land/2015/08/31/effective-cpp-33.html。如有疏漏、谬误、侵权请通过评论或 邮件 指出。