1. 函数重载(Overload):
普通的重载我们都知道是怎么回事。在面向对象的编程中,函数重载只能发生在同一个类当中!
典型的重载重载就是类的构造函数,比如:无参构造函数,拷贝构造函数,赋值构造函数之间。
重载发生的条件是:仅仅函数的形参(数量或者类型)不同。
2. 函数覆盖/重写(Override):
函数覆盖(也就是重写)就是我们平时所说的类的多态性实现的时候发生的事情:
基类指针指向派生类,指针调用函数时派生类的函数就会覆盖基类函数。
覆盖发生的条件,就是多态性实现的条件:
a.基类指针指向派生类;
b.基类的函数前有virtual声明为虚函数;
c.基类和派生类(函数名和参数等)完全相同!
3. 函数隐藏(Hide?):
其他类定义的函数,被类的同名函数屏蔽,好像被隐藏了。
(说成被覆盖了好像也解释得通,但是人家规定好“覆盖”和“隐藏”的意义了,就入乡随俗好了)
函数隐藏的条件:
a.首先要同名,不然就不是同一个函数了;
b.不同类的同名函数,假如满足函数覆盖,就会发生函数隐藏!
虽然b听起来很绝对,但是也很好记,关键是正确!
4. 举个栗子好了:
1 class Base 2 { 3 public: 4 virtual void f(float x) { cout << "Base::f(float)" << endl; } 5 void g(float x) { cout << "Base::g(float)" << endl; } 6 void h(float x) { cout << "Base::h(float)" << endl; } 7 } 8 9 class Derive 10 { 11 public: 12 virtual void f(float x) { cout << "Derive::f(float)" << endl; } 13 void g(int x) { cout << "Derive::g(int)" << endl; } 14 void h(float x) { cout << "Derive::h(float)" << endl; } 15 }
调用:
Base *base = new Derive;
base -> f(1.0);
base -> g(2);
base -> h(1.0);
会发生什么?根据上面的规则很好判断:
先看是否符合多态性的条件,只有Derive::f(float x)符合,所以会发生函数覆盖;
其他不符合的,就是发生函数隐藏,也就是由于指针指向派生类,说此时函数的调用只和派生类有关,和基类没有关系!
5. 覆盖和隐藏的选择
一间学校,基类是学生,派生类是:小学生,初中生,高中生;
函数有:吃饭,上课,考试;
吃饭是每个学生都要做的事情,而且做法一样,都是去食堂吃饭;
考试是部分学生(初高中生)需要做的事情,小学生减负不考试;
上课虽然是每类学生都要做的事情,但是每类学生的做法都不相同;
吃饭,考试,上课,对于每个派生类的共用性程度越来越低,这种情况下如何设定函数的模式呢?
首先,既然每个派生类都要实现相同的吃饭函数,就把吃饭放在基类中,作为基类的普通成员函数。
派生类继承基类,自然就可以正常使用吃饭函数。
这叫做什么?就是普通的继承啊~
然后,上课是每个派生类都做但是做法都不相同的函数,很容易想到通过多态性实现。
具体来说就是在基类中声明上课是虚函数,然后在不同派生类中重写自己的上课函数,进行覆盖。
最好声明上课为纯虚函数,就不用在基类中实现上课函数了,因为基类的上课本来就没有特殊意义,只是一个接口。
这样基类就成了抽象类,不能实例化。
不过没关系,我们建议用指针实现类的实例化和函数调用。
最后,有大部分派生类可以使用考试函数,但是又不能让小学生考试,这种情况下怎么设定函数模式?
肯定不能是基类的普通成员函数,否则怎么实现?
第一种方法:
在基类中声明并实现考试函数为虚函数,在大部分派生类中不定义考试函数进行覆盖,
只在小学生类中重新定义一个空的考试函数,进行覆盖以实现多态性。
第二种方法:
在基类中定义考试为普通成员函数,派生类通过继承使用她。
对于小学生来说,可以在类中重新定义一个空的考试函数,实现对基类考试函数的隐藏。
从逻辑性来说,实现多态性会更容易理解一些。