浅谈C++中对于函数的overload(重载)、override(覆盖)、overwrite(重写,也有翻译为隐藏、重定义、屏蔽、隐藏)的理解。
这三个概念很容易混淆,面试题常见题目,也是本人面试百度过程中被问到的一个题目。但是我认为overload和overwrite是虚假的概念。在Java和Scala中,甚至不存在overwrite的概念。overload可以看作是普通函数,唯一特别的是普通函数具有相同的函数名。所有这些都是在编译时确定的。
而override对于多态性非常有用。被调用的方法是在运行时确定的。我认为overwrite的概念可能来自于c++中关键字virtual的使用。当使用virtual分配基类方法时,override将取得调用后期绑定的效果。或者这些方法将被早期绑定,并且不存在多态行为。
英文定义:
- 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
- overload: function overloading means:
- the same range (in the same class)
- the same function name
- different function signature
- 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
翻译为中文定义
- overload:是指同一可访问区内被声明的几个具有不同参数列(参数的类型、个数、顺序不同)的同名函数,根据参数列表确定调用哪个函数,重载解析中不考虑返回类型,而且在不同的作用域里声明的函数也不算是重载。
特点:
1)同名不同参:两个同名同参的函数绝不是重载,注意参数有无const也视为不同;
2)仅仅返回值不同,不构成重载;
3)相同作用域:同一文件域/全局域/namespace域内的同名不同参函数可以构成重载;而对于类成员函数,重载必须发生在同一类域中,分属于基类与派生类的函数不能直接构成重载(派生类函数重载基类函数的说法错误)。
1 | //示例代码 |
- override:是指派生类中存在重新定义的函数。其函数名、参数列表、返回值类型,所有都必须同基类中被重写的函数一致。只有函数体不同(花括号内),派生类调用时会调用派生类的重写函数,不会调用被重写函数。重写的基类中被重写的函数必须有virtual修饰。
特点:
1)不同作用域(分别位于派生类与基类);
2)基类函数须为virtual函数,而派生类重新实现的函数需与基类虚函数同名同参。
1 | //示例代码 |
- overwrite:指派生类的函数屏蔽与其同名的基类函数,发生在不同类域(派生类与基类之间),具体情形包括:
1)派生类与基类函数同名但不同参,此时不论基类函数是否virtual,其都将被派生类函数隐藏。(不要与重载混淆,重载发生在同一类域)
2)派生类函数与基类非virtual函数同名同参,此时基类函数被隐藏(不要与override混淆,override特指基类函数为virtual的情况)
1 | //示例代码 |
梳理后的逻辑:
类public派生时,基类成员全部继承到派生类,除非派生类有同名;当派生类与基类成员有同名,除了基类成员为virtual函数时属于覆盖(override),其他情况都是隐藏(overwrite)。
覆盖与隐藏的区别:
1 | class Base |
例中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》