1) 重载的特征:
a) 相同的范围(在同一个类中)
b) 函数名字必须相同
c) 参数不同(指参数类型不同,或参数个数不同,或两者皆有)d) virtual关键字可有可无
例子如下:
class Base{public:void fun(void);int fun(int a);int fun(double b);int fun(int a, int b);int fun(int a, double b);};上面Base类里fun()属于重载函数,主要函数的重载只是函数参数有关,和函数的返回值无关的。如下面的例子,只是函数的返回值不同,不属于函数的重载,编译器会报错。class Base{public:void fun(int a);double fun(int a);int fun(int a);};
2) 隐藏是指派生类的函数屏蔽了与其同名的基类函数。
规则如下:
a)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字, 基类的函数将被隐藏(注意别与重载混淆)。b)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与重写(覆盖)混淆)。
例子:
(a)
class Base{public:
// virtual关键字可有可无
void fun(int n){cout << "in the Base" << endl;}
};class Derived: public Base{public:
// 注意参数和基类的是不同的void fun(void){
cout << "in the Derived" << endl;
}
};/*这时候,尽管派生类Derived公共继承于基类Base,但是派生类Derive隐藏基类Base的fun(int n)的函数接口,Derive类的内部只有fun(void)函数。只要派生类Derive的fun()函数参数和基类的不一样(和函数返回值无关),派生类就会隐藏基类的同名函数。*/int main(void){
Derived test;test.fun(10);// 错误,Derived类没fun(int)这个成员函数test.fun(); // 结果为:"in the Derived"return 0;
}(b)class Base{public://注意:没有virtual关键字void fun(int n){cout << "in the Base" << endl;}};class Derived: public Base{public:// 参数和基类相同void fun(int n){cout << "in the Derived" << endl;}};int main(void){Derived test;test.fun(10);/*结果为:"in the Derived"不是基类Base里的"in the Base",因为派生类隐藏了基类同名的函数。*/return 0;}
3) 重写(覆盖)的特征有:
a) 不同的范围(分别位于派生类与基类)
b) 函数名字必须相同
c) 参数必须相同
d) 基类函数必须有virtual关键字
例子:
#include <iostream.h>class Base{public:void f(int x){ cout << "Base::f(int) " << x << endl; }void f(float x){ cout << "Base::f(float) " << x << endl; }// 必须有virtual关键字virtual void g(void){ cout << "Base::g(void)" << endl;}};class Derived : public Base{public:// virtual关键字,可有可无virtual void g(void){ cout << "Derived::g(void)" << endl;}};void main(void){Derived d;Base *pb = &d;pb->f(42); // 运行结果: Base::f(int) 42pb->f(3.14f); // 运行结果: Base::f(float) 3.14pb->g(); // 运行结果: Derived::g(void) (动态联编)}
综合例子:
#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),而不是覆盖。看完前面的示例,可能大家还没明白隐藏与覆盖到底有什么区别,因为我们前面都是讲的表面现象,怎样的实现方式,属于什么情况。下面我们就要分析覆盖与隐藏在应用中到底有什么不同之处。在下面的程序中bp和dp指向同一地址,按理说运行结果应该是相同的,可事实并非如此。void main(void){Derived d;Base *pb = &d;
// Bad : behavior depends on type of the pointerDerived *pd = &d; // Good : behavior depends solely on type of the object// f()为虚函数,动态联编pb->f(3.14f); //运行结果: Derived::f(float) 3.14pd->f(3.14f); //运行结果: Derived::f(float) 3.14pb->g(3.14f); //运行结果: Base::g(float) 3.14pd->g(3.14f); //运行结果: Derived::g(int) 3pb->h(3.14f); //运行结果: Base::h(float) 3.14pd->h(3.14f); //运行结果: Derived::h(float) 3.14}请大家注意,f()函数属于覆盖,而g()与h()属于隐藏。从上面的运行结果,我们可以注意到在覆盖中,用基类指针和派生类指针调用函数f()时,系统都是执行的派生类函数f(),而非基类的f(),这样实际上就是完成的“接口”功能。而在隐藏方式中,用基类指针和派生类指针调用函数f()时,系统会进行区分,基类指针调用时,系统执行基类的f(),而派生类指针调用时,系统“隐藏”了基类的f(),执行派生类的f(),这也就是“隐藏”的由来。