那天在一问一答上碰到一道题:
下面程序的输出?
#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)