C++中派生类函数遮挡(Hide)基类中同名函数的问题是比较令人费解的,有必要详细说明一下。
看看下面一段代码:
class Base
{
public:
virtual void f(int x) {};
};
class Derived : public Base
{
public:
virtual void f(void* p) {};
};
Derived *pd = new Derived;
pd->f(10); //编译错误
对于这样一种情况,Scott Meyers这样解释(Effective C++中文第二版 第50条),Derived::f遮蔽了Base::f,即使两者的参数类型并不相同。也就是说编译器不知道有一个参数类型为int的Base::f存在。
但是下面的代码却可以编译通过:
Base *pb = new Derived;
pb->f(10);
Bjarne Stroustrup在ARM中对此的解释是:假设当你调用f时,你真正想调用的时Derived版本,但你意外地使用了错误的参数类型(本例为int)。更进一步假设Derived是继承体系中的一员,而且你并不知道Derived间接继承了某个BaseClass,后者声明有一个虚拟函数f,需要一个int参数。这种情况下你将意外调用BaseClass::f——一个你甚至并不知道它存在的函数。这种错误有可能在大型继承体系中常常发生,所以Stroustrup决定釜底抽薪(好一个釜底抽薪!)地令derived class members遮蔽同名的base class members。
再看看以下代码:
class Base
{
public:
virtual void f(int x) {};
virtual void f(char* x) {};
};
class Derived : public Base
{
public:
virtual void f(int x){};
};
Derived *pd = new Derived;
pd->f((char*)2); // 编译错误
pd->f(2); // 通过
Base *pb = new Derived;
pb->f((char*)2); // 通过
pb->f(2); // 通过
根据以上的解释,应该不难理解这样的编译结果。那么我们看看静态函数是否一样的情况。
class Base
{
public:
static void f(int x) {};
};
class Derived : public Base
{
public:
static void f(void* x) {}; //不论此定义前是否有 static,都有以下结果
};
Derived *pd = new Derived;
pd->f(2); // 编译错误
pd->f((void*)2); // 通过
Base *pb = new Derived;
pb->f(2); // 通过
pb->f((void*)2); // 错误
这样我们发现对于static函数,一样存在如上所述的遮蔽作用。C++允许派生类重新定义基类中的任何函数(private除外),这种权力是一把双刃剑,在给予程序员能力的同时也诱导他们犯错误。
class Base
{
public:
static void f(int x) {};
};
class Derived : public Base
{
public:
virtual void f(int x) {};
};
class Son : public Derived
{
public:
virtual void f(int x){};
};
Son *ps = new Son;
Derived *pd = ps;
Base *pb = pd;
ps->f(2); // 调用 Son::f
pd->f(2); // 调用 Son::f
pb->f(2); // 调用 Base::f
以上这种在开发继承层次很多的大型软件时表现出来的精神分裂症就很难找到问题根源。