那天在一问一答上碰到一道题:
下面程序的输出? #include <iostream> using namespace std; static int x = 1; static int y = 2; struct A { static int x; static int y; }; int A::x = 3; int A::y = x; int main(void) { cout << A::y << endl; return 0; }先不说题目的正确答案,我当时的思路是 碰到int A::y =x 这条语句时,是将全局变量x的值赋给了结构体A中的成员变量y,所以应该是1,但是实际并不是这样的(也许题目看起来是这样的,但其实里面隐藏着一个陷阱),在得到正确答案之后,我想起来曾经看过的<<Effective C++>>一书当中,关于这部分内容的详细解答:
在诸如以下的代码中:
int x; //global variable int SomeFun(){ double x; std::cin>>x; }这个读取数据的语句涉及的是local变量x,而不是global变量,因为内层作用于的名字会掩盖(遮盖)外围作用于的名字。
当编译器处于SomeFun的作用域内并遭遇x时,它首先在local作用域内寻找是否有什么东西带着这个名称,如果找不到,就继续依次向外围作用于查找,如果找到,就不再找其他作用域。 本例中的local x是double的,但是global中x是int型的,但是不要紧,C++的名称掩盖规则(name-hiding rules)唯一做的一件事是:掩盖名称。至于类型是否相同,并不重要。于是本例中,一个double型的x掩盖了一个int型的x。
我们再来看一个复杂点的例子:
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(); //correct! 调用dervied类的mf1 d.mf1(x); // Wrong! 因为Derived类的mf1掩盖了Base类的mf1 d.mf2(); //correct! 调用Base::mf2 d.mf3(); //correct! 调用Derived::mf3 d.mf3(x); //Wrong!如我们所看到的,即使base class和derived class内的函数有不同的参数类型也适用,而且不论函数是virtual还是non-virtual一体适用。
这些行为的背后的基本理由是为了防止你在程序库 或者应用框架(application framework)内建立新的derived class时附带的从疏远的base class继承重载函数。而实际上如果我们正在适用public继承而又不继承那些重载函数,就违反了base和derived class之间的is-a 关系,而 is-a 是public继承的基石,为此,我们可以适用using声明式达成目标:
class Derived: public Base{ public: using Base::mf1; //让Base class内名为mf1和mf3的所有东西在 using Base::mf3; //Derived作用域内都可见 virtual void mf1(); virtual mf3(); void mf4(); ... };
现在所有的继承机制就可以正常运行了!
有时候我们并不想继承base class内的所有函数,这也是可以理解的,但是要记住,在public继承下,这是绝对不可能的,因为它违反了public继承下derived 和base class之间的is-a 关系,(这也就是为什么上述using声明式呗放在derived class的public区域的原因,base class内的public名称在publicly derived class内也应该是public)。
然而在private继承下,它可能有意义。例如derived以private继承自base,儿derived唯一想继承的mf1是那个无参数版本。using声明式在此处排不上用处了,因为它会令继承而来的某给定名称的所有同名函数在derived class内为可见。因此我们需要一个简单的转交函数(forwarding function)
class Derived: private Base{ public: virtual void mf1() {Base::mf1();} //转交函数,暗自成为inline函数 ... }; Derived d; int x; d.mf1(); //正确,调用derived::mf1 d.mf1(x); //错误,因为base::mf1被掩盖了
1. derived class内的名称会遮掩base class内的名称,在public继承下没有人希望如此
2. 为了让掩盖的名称再见天日,可使用using声明式货转交函数(forwarding function)