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

来源:互联网 发布:蜡笔小新德朗医生 知乎 编辑:程序博客网 时间:2024/05/01 04:52
我们都知道,内层作用域会掩盖外层作用域的名称。至于名称类型是否相同并不重要,如x的int掩盖x的double.现在引入继承,当派生类成员函数涉及基类的某物(成员函数,typedef,或成员变量),编译器可以找出所涉及的东西,派生继承了声明于基类的所有东西,实际运作方式是:Drived作用域嵌套于Base类作用域内。如下:
class Base{private:         int x;public:         virtualvoid mf1()=0;         virtualvoid mf2();         void mf3();         …};class Derived:public Base{public:         virtualvoid mf1();         void mf4();         …};

上述代码虽然涉及不同类型的函数及公私方式,这是为强调我们谈的是名称,和其他无关。
考虑下面派生类mf4的实现代码:
void Derived::mf4(){         mf2();         ….}
编译器查找规则:
local作用域(mf4覆盖的作用域)->Derived class覆盖作用域->Base作用域(本例已找到)->内含Base的那个namespace作用域(如果有)->全局作用域(global)
再考虑一个例子,重载mf1和mf3,然后在derived中添加一新的mf3:
class Base{private:         int x;public:         virtualvoid mf1()=0;         virtualvoid mf1(int);         virtualvoid mf2();         void mf3();         voidmf3(double);         …};class Derived:public Base{public:         virtualvoid mf1();         void mf3();         void mf4();         …};

上述从名称查找规则看:基类所有名为mf1和mf3都被掩盖,故不再继承。
Derived d;int x;d.mf1();    //T   Derived::mf1();d.mf1(x);  //F   Derived::mf1掩盖Base::mf1d.mf2();             //T  Base::f2d.mf3();     //T   Derived::mf3d.mf3(x);   //F   Derived::mf3掩盖Base::mf3

显然,上述规则对所有不同参数类型适用,对虚函数与否也适用。
但有时候也会想继承重载函数,实际上:如果正在使用实际public继承却又不能继承重载函数,就是违反了B和D的is-a关系。我们可以使用using 声明达成目标:
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:         using Base::mf1;  //让B类中名为mf2和mf3的函数在D内作用域内都可见(且为public)         using Base::mf3;         virtual void mf1();         void mf3();         void mf4();         …};

现在继承机制一如继往:
Derived d;int x;d.mf1();    //T   Derived::mf1();d.mf1(x);   //T   Base::mf1d.mf2();             //T  Base::f2d.mf3();     //T   Derived::mf3d.mf3(x);    //T   Base::mf3

这意味着如果想继承基类并加上重载函数,但又想重新改写其中一部分,我们可以为那些原本会遮掩的每个名称引入一个using声明式。
但有时又不想继承基类所有函数,但在public继承中绝对不可能发生,因为public继承意味着B和D这间的is-a关系。基类的public名称在public派生类内也应该是public(这也是using声明放入D内public域内原因)
但在private继承中却是有意义的,如我们想继承的那个是无参的mf1版本,此时using声明无用。因为using声明式会令继承而来的给定名的所有同名函数在D中可见,我们可以采用不同方法,即简单的转交函数,如下:
class Base{public:         virtualvoid mf1()=0;         virtualvoid mf1(int);         …  //同前};class Derived:public Base{public:         virtualvoid mf1() //转交函数(forwarding function){using Base::mf1();} //暗自成为inline  (TK30)         …};….Derived d;int x;d.mf1();  //T  调用Derived::mf1();    d.mf1(x);  //F    Base::mf1被遮掩

需要记住的:
1、derived类内的名称会掩盖Base内的名称。在public继承下没人希望会这样。
2、为了让被掩盖的名称再重新可见,可以使用using声明式或转交函数。

0 0