重载、覆盖和隐藏的区别
重载(overload) :就是函数或者方法有相同的名称,但是参数列表不相同的情形,这样的同名不同参数的函数或者方法之间,互相称之为重载函数或者方法。
多个重载函数在调用的时候根据函数的参数来区别不同的函数。
关键点:函数名相同,参数表不同
覆盖(override) :是指在派生类中重新对基类中的虚函数重新实现,即函数名和参数都一样,只是函数的实现体不一样。
关键词:派生类中,虚函数,函数名和参数表完全相同
隐藏(hide) :派生类中的函数把基类中相同名字的函数屏蔽掉了。
隐藏一词可以这么理解:在调用一个类的成员函数的时候,编译器会沿着类的继承链逐级的向上查找函数的定义,如果找到了那么就停止查找了,所以如果一个派生类和一个基类都有同一个同名(暂且不论参数是否相同)的函数,而编译器最终选择了在派生类中的函数,那么我们就说这个派生类的成员函数"隐藏"了基类的成员函数,也就是说它阻止了编译器继续向上查找函数的定义.
关于这三种情况的示例代码如下
#include <iostream.h>
class Base
{
public:
virtual void f(float x)
{
cout << "Base::f(float) " << x << endl;
}
void g(float x)
{
cout << "Base::g(float) " << x << endl;
}
void h(float x)
{
cout << "Base::h(float) " << x << endl;
}
};
class Derived : public Base
{
public:
virtual void f(float x)
{
cout << "Derived::f(float) " << x << endl;
}
void g(int x)
{
cout << "Derived::g(int) " << x << endl;
}
void h(float x)
{
cout << "Derived::h(float) " << x << endl;
}
};
从以上代码能够看出:
(1)函数Derived::f(float)覆盖了Base::f(float)。
(2)函数Derived::g(int)隐藏了Base::g(float),而不是重载。
(3)函数Derived::h(float)隐藏了Base::h(float),而不是覆盖。
考虑代码在运行时的结果:
void main(void)
{
Derived d;
Base *pb = &d;
Derived *pd = &d;
// Good : behavior depends solely on type of the object
pb->f(3.14f); // Derived::f(float) 3.14
pd->f(3.14f); // Derived::f(float) 3.14
// Bad : behavior depends on type of the pointer
pb->g(3.14f); // Base::g(float) 3.14
pd->g(3.14f); // Derived::g(int) 3 (surprise!)
// Bad : behavior depends on type of the pointer
pb->h(3.14f); // Base::h(float) 3.14 (surprise!)
pd->h(3.14f); // Derived::h(float) 3.14
}
在第一种调用中,函数的行为取决于指针所指向的对象。在第二第三种调用中,函数的行为取决于指针的类型。所以说,隐藏破坏了面向对象编程中多态这一特性,会使得开发人员产生混乱。
函数接口设计注意要点
在设计基类的函数接口时,不要出现既是虚函数,又对该虚函数进行了重载的情况。这样会对派生类的多态函数实现造成不便。
示例代码如下
class Base
{
virtual foo();
virtual foo(int n);
}
class Derived : public Base
{
virtual foo();
}
从以上代码能够看出:
-
Derived::foo()
与Base::foo()
构成了多态关系
-
Derived::foo()
隐藏了Base::foo(int)
所以,如果派生类希望实现 foo()
函数的多态,也就必须实现 foo(int)
的多态,否则就会出现隐藏。
如果在现有代码中已经发生了上述 Base
的情况,在不改变原有代码的情况下,可以使用在派生类中使用这样的语法 using Base::foo;
,这会将基类中所有的名为 foo
的函数引入到派生类中来。