浅谈C++中对于函数的overload(重载)、override(覆盖)、overwrite(重写,也有翻译为隐藏、重定义、屏蔽、隐藏)的理解。

这三个概念很容易混淆,面试题常见题目,也是本人面试百度过程中被问到的一个题目。但是我认为overload和overwrite是虚假的概念。在Java和Scala中,甚至不存在overwrite的概念。overload可以看作是普通函数,唯一特别的是普通函数具有相同的函数名。所有这些都是在编译时确定的。

而override对于多态性非常有用。被调用的方法是在运行时确定的。我认为overwrite的概念可能来自于c++中关键字virtual的使用。当使用virtual分配基类方法时,override将取得调用后期绑定的效果。或者这些方法将被早期绑定,并且不存在多态行为。

英文定义:

  1. override: subclass method overrides base class method means:
  • in different range (in derived class and base class)
  • the same function name
  • the same function signature
  • the return type conforms covariance
  • the base class method is virtual
  1. overload: function overloading means:
  • the same range (in the same class)
  • the same function name
  • different function signature
  1. overwrite: subclass method hides base class method means:
  • in different range (in derived class and base class)
  • the same function name
  • two cases on parameters ( signature? ):
    • the same parameters, the base class method is not virtual
    • different parameters

翻译为中文定义

  1. overload:是指同一可访问区内被声明的几个具有不同参数列(参数的类型、个数、顺序不同)的同名函数,根据参数列表确定调用哪个函数,重载解析中不考虑返回类型,而且在不同的作用域里声明的函数也不算是重载。

特点:
1)同名不同参:两个同名同参的函数绝不是重载,注意参数有无const也视为不同;
2)仅仅返回值不同,不构成重载;
3)相同作用域:同一文件域/全局域/namespace域内的同名不同参函数可以构成重载;而对于类成员函数,重载必须发生在同一类域中,分属于基类与派生类的函数不能直接构成重载(派生类函数重载基类函数的说法错误)。

1
2
3
4
5
6
7
8
9
//示例代码
class A{
public:
void func(int i);
void func(double i);//overload
void func(int i, double j);//overload
void func(double i, int j);//overload
int func(int i); //非重载。注意重载考虑函数返回类型。
};
  1. override:是指派生类中存在重新定义的函数。其函数名、参数列表、返回值类型,所有都必须同基类中被重写的函数一致。只有函数体不同(花括号内),派生类调用时会调用派生类的重写函数,不会调用被重写函数。重写的基类中被重写的函数必须有virtual修饰。

特点:
1)不同作用域(分别位于派生类与基类);
2)基类函数须为virtual函数,而派生类重新实现的函数需与基类虚函数同名同参。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//示例代码
#include<iostream>
using namespace std;

class Base
{
public:
virtual void fun(int i){ cout << "Base::fun(int) : " << i << endl;}
};

class Derived : public Base
{
public:
virtual void fun(int i){ cout << "Derived::fun(int) : " << i << endl;}
};
int main()
{
Base b;
Base * pb = new Derived(); //向上造型
pb->fun(3); //Derived::fun(int)

delete pb;
system("pause");
return 0;
}

  1. overwrite:指派生类的函数屏蔽与其同名的基类函数,发生在不同类域(派生类与基类之间),具体情形包括:
    1)派生类与基类函数同名但不同参,此时不论基类函数是否virtual,其都将被派生类函数隐藏。(不要与重载混淆,重载发生在同一类域)
    2)派生类函数与基类非virtual函数同名同参,此时基类函数被隐藏(不要与override混淆,override特指基类函数为virtual的情况)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//示例代码
class Base{
public:
virtual int fcn();
};
class D1:public Base{
public:
//隐藏基类的fcn,这个fcn不是虚函数
//D1继承了Base::fcn()的定义
int fcn(int); //形参类别与Base中的fcn不一样
virtual void f2(); //是一个新的虚函数,在Base中不存在
};
class D2:public D1{
public:
int fcn(int); //是一个非虚函数,隐藏了D1::fcn(int)
int fcn(); //覆盖了Base的虚函数fcn
void f2(); //覆盖了D1的虚函数f2

梳理后的逻辑:

类public派生时,基类成员全部继承到派生类,除非派生类有同名;当派生类与基类成员有同名,除了基类成员为virtual函数时属于覆盖(override),其他情况都是隐藏(overwrite)。

覆盖与隐藏的区别:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class Base

{

virtual void a(int x){ cout << "Base::a(int) "<< x << endl;}

void b(int x){ cout << "Base::b(int) "<< x << endl; }

};

class Drv0 : public Base

{

virtual void a(int x){ cout << "Drv0::a(int) "<< x << endl; }

void b(int x){ cout << "Drv0::b(int) "<< x << endl; }

};

void main()

{

Drv0 d;

Base *pbase = &d;

Drv0 *pdrv = &d;



pbase->a(3); //输出Drv0::a(int) 3

pdrv->a(3); // 输出Drv0::a(int) 3



pdrv->b(3); // 输出Drv0::b(int) 3

pbase->b(3); // 输出Base::b(int) 3

}

例中Drv0::a(int)覆盖(override)了Base::a(int),因此不管是通过基类指针调用pbase->a(3),还是通过派生类指针调用pdrv->a(3),最终都是根据对象类型(Drv0 d;)选择调用函数,结果都为Drv0::a(int) 3

而Drv0::b(int)则隐藏(overwrite)了Base::b(int),外在表现就是根据指针类型(Base* pbase和Drv0* pdrv)决定调用函数,即pdrv->b(3)输出Drv0::b(int) 3,而pbase->b(3)输出Base::b(int) 3。因此隐藏(overwrite)相当于对于某一功能派生类另起炉灶,基类和派生类各干各的。

所以覆盖(override)的结果是根据对象类型决定调用哪个函数(多态);而隐藏(overwrite)则表现为根据指针类型决定调用哪个函数,这是覆盖和隐藏最直观的区别。

隐藏(overwrite)是对函数继承的破坏,多数情况没有必要,因为基类中非virtual型成员函数本就代表类的功能中不变的部分,派生类 应该直接继承使用而不需修改,发生overwrite多数说明设计不完善。

实际上很多时候是不小心把override实现成了overwrite,即本该把基类函数设计为virtual以实现可变功能,却忘了写virtual,结果当在派生类中重写同名函数时就成了overwrite隐藏。

致谢

Override Overload Overwrite
半山茶馆C基础篇–overload重载&override覆盖&overwrite隐藏
一首小夜曲C overload、override、overwrite