我们都知道,内层作用域会掩盖外层作用域的名称。至于名称类型是否相同并不重要,如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::mf1 d.mf2(); //T Base::f2 d.mf3(); //T Derived::mf3 d.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::mf1 d.mf2(); //T Base::f2 d.mf3(); //T Derived::mf3 d.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声明式或转交函数。
条款33、避免遮掩继承而来的名称
最新推荐文章于 2023-12-28 13:35:03 发布