重载和覆盖的区别
文章目录
重载
重载是指函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。在同一可访问区域内被声明的几个具有不同参数列(参数的类型、个数、顺序不同)的同名函数,程序会根据不同的参数列来确定具体调用哪个函数。对于重载函数的调用,在编译期间就己经确定,是静态的,它们的地址在编译期间就绑定了与多态无关。注意,重载不关心函数的返回值类型。
1) double calculate(double);
2) double calculate(double, double);
3 ) double calculate(double, int);
4) double calculate(int, double);
5 ) double calculate(int);
1) float calculate(float);
2) float calculate(double);
7个同名函数calculate,1)、2)、3)、4)、5)、6)中任两个均构成重载,6)和7)也能 构成重载,而1)和7)却不能构成重载,因为1)和7)的参数相同。
成员函数被重载的特征如下:
- 相同的范围(在同一个类中)。
- 函数名字相同。
- 参数不同。
覆盖
覆盖是指派生类中存在重新定义基类的函数,其函数名、参数列、返回值类型必须同父类中的相对应被覆盖的函数严格一致,覆盖函数和被覆盖函数只有函数体不同,当派生类对象调用子类中该同名函数时会自动调用子类中的覆盖版本,而不是父类中的被覆盖函数版本,它和多态真正相关。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态地调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法出)。因此,这样的函数地址是在运行期绑定的。
覆盖的特征如下:
- 不同的范围(分别位于派生类与基类)。
- 函数名字相同。
- 参数相同。
- 基类函数必须有virtual关键字。
- 如果返回值类型是基本数据类型,必须相同,如果返回类型是个指针,指向一个基类,那么派生类的函数可以返回一个指针,指向该基类的一个派生类.只要是父类返回类型的子类就行,这叫做协变返回.
注意:如果返回值不符合规则,无法完成编译.
重载与覆盖的区别
-
覆盖是子类和父类之间的关系,是垂直关系;重载是同一个类中方法之间的关系,是水平关系。
-
覆盖只能由一个方法,或只能由一对方法产生关系;方法的重载是多个方法之间的关系。
-
覆盖要求参数列表相同;重载要求参数列表不同。
-
覆盖关系中,调用方法体是根据对象的类型(对象对应存储空间类型)来决定的,重载关系是根据调用时的实参表与形参表来选择方法体的。
隐藏
隐藏是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
-
如果派生类的函数与基类的函数同名,但是参数不同,则不论有无virtual关键字,基类的函数都将被隐藏。
-
如果派生类的函数与基类的函数同名,并且参数也相同,基类函数没有virtual关键字,此时基类的函数被隐藏。
在调用一个类的成员函数时,编译器会沿着类的继承链逐级地向上查找函数的定义,如果找到了就停止查找了。所以,如果一个派生类和一个基类都存在同名(暂且不论参数是否相同)的函数,而编译器最终选择了在派生类中的函数,那么就说这个派生类的成员函数“隐藏”了基类的成员函数,也就是说它阻止了编译器继续向上查找函数的定义。
回到隐藏的定义中,前面已经说了有virtual关键字,并且派生类函数与基类函数同名,同参数函数构成覆盖的关系,因此隐藏的关系只有如下的可能:
- 必须分别位于派生类和基类中。
- 必须同名。
- 参数不同的时候本身已经不构成覆盖关系了,所以此时是否是virtual函数己经不重要了。
- 当参数相同,返回值符合规则的时候就要看是否有virtual关键字了,有的话就是覆盖关系,没有的时候就是隐藏关系了。
程序代码示例如下:
|
|
程序输出结果:
Derived::f(float) 3.14
Derived::f(float) 3.14
Base::g(float) 3.14
Derived::h(float) 3.14
上例中,函数 Derived::f(float)覆盖了 Base::f(float),函数 Derived::g(int)隐藏了Base::g(float),而不是重载;函数Derived::h(float)隐藏了 Base::h(float),而不是覆盖。
文章作者 Forz
上次更新 2017-09-01