一、 重载(overload)
成员函数被重载(overload)的特征:
(1)相同的范围(在同一个类中);
(2)函数名字相同;
(3)参数不同;
(4)virtual 关键字可有可无。
二、重写(override)
覆盖是指派生类函数覆盖(override)基类函数,特征是:
(1)不同的范围(分别位于派生类与基类);
(2)函数名字相同;
(3)参数相同;
(4)基类函数必须有 virtual 关键字。
三、隐藏
#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; }
};
void main(void)
{
Derived d;
Base *pb = &d;
Derived *pd = &d;
pb->f(3.14f); // Derived::f(float) 3.14
pd->f(3.14f); // Derived::f(float) 3.14
pb->g(3.14f); // Base::g(float) 3.14
pd->g(3.14f); // Derived::g(int) 3 (surprise!)
pb->h(3.14f); // Base::h(float) 3.14 (surprise!)
pd->h(3.14f); // Derived::h(float) 3.14
}
首先说明:基类指针可以指向一个派生类对象,但派生类指针不能指向基类对象。
基类类型指针可以指向派生类型对象但是无法使用不存在于基类只存在于派生类的元素。(所以我们需要虚函数和纯虚函数)
原因:
在内存中,一个基类类型的指针是覆盖N个单位长度的内存空间。当其指向派生类的时候,由于派生类元素在内存中堆放是:前N个是基类的元素,N之后的是派生类的元素。于是基类的指针就可以访问到基类也有的元素了,但是此时无法访问到派生类(就是N之后)的元素。
【代码实例解析】
基类中pb->f(float)被子类相同函数覆盖,所以pb->f(3.14f)调用的为子类f()函数
pd->f(float)自然调用自身f()函数。
pb->g(3.14f)只能访问对象中基类包含的内存区,故调用基类g()函数
pd->g(3.14f),pd首先在派生类包含区中进行函数匹配,匹配到了同名函数(尽管参数不一致,但是可以隐式转换)Derived::g(int),则基类中更吻合的函数Base::g(float)被忽略,可看做被派生类对象隐藏。
实际上如果派生类中没有同名函数g(),pd->g(3.14)将调用基类中的对应函数;如果实参不能隐式转换为派生类中对应形参类型,则编译报错。
pb->h(3.14f);只能访问对象中基类包含的内存区,故调用基类h()函数
pd->h(3.14f);查找派生类中函数时即发现同名函数Derived::h(),则调用该函数,可看做基类的h()函数被派生类对象隐藏。
总结:隐藏是相对于派生类对象而言
隐藏规则:
(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无 virtual关键字,基类的函数将被隐藏。
(2)如果派生类的函数与基类的函数同名,并且参数也相同, 但是基类函数没有 virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。
基类的同名函数被隐藏后,如果实参类型不能隐式转换成派生类对应函数形参类型,则报错,尽管基类对应被隐藏函数数据类型更吻合,但是没办法,它被隐藏了。