2024-03-01 11:01:05
Effective C++知识点36:绝不要重新定义继承而来的non-virtual函数
在C++中,继承是面向对象编程中的一个核心概念,它允许我们基于已有的类(基类或父类)来构建新的类(派生类或子类)。然而,在继承体系中重新定义基类中的成员函数时,需要特别小心,尤其是对于那些非虚函数(non-virtual函数)。
答案核心:绝不要重新定义继承而来的non-virtual函数,因为这样做会导致基类的同名函数被“遮蔽”,从而引发意料之外的行为。
详细解释:
遮蔽效应:
当在派生类中重新定义了一个基类的非虚函数时,这个派生类的函数会遮蔽(hide)基类中的同名函数。
遮蔽意味着,通过派生类的对象或引用调用该函数时,将总是调用派生类中的版本,而基类中的版本则无法被直接访问(除非通过特定的类型转换)。
静态绑定与动态绑定:
非虚函数(non-virtual函数):这些函数在编译时确定调用哪个版本,称为静态绑定(static binding)或早期绑定(early binding)。
即使你有一个基类指针或引用,指向一个派生类对象,并通过这个指针或引用调用非虚函数,调用的仍然是基类中的版本(如果基类中有这个函数的定义)。
虚函数(virtual函数):这些函数在运行时确定调用哪个版本,称为动态绑定(dynamic binding)或晚期绑定(late binding)。
通过基类指针或引用调用虚函数时,会根据指针或引用实际指向的对象类型来确定调用哪个版本的函数。
为什么不要重新定义继承而来的non-virtual函数:
预期行为与实际行为不符:如果你期望通过基类指针或引用来调用派生类中重定义的函数,但由于该函数是非虚的,你实际上会调用到基类中的版本。这会导致程序的行为与你的预期不符。
代码可读性和可维护性降低:重新定义非虚函数会使代码的逻辑变得更加复杂和难以理解。其他开发者在阅读你的代码时,可能会因为不知道存在这样的遮蔽效应而陷入困惑。
潜在的bug:由于静态绑定的特性,重新定义非虚函数很容易引入难以察觉的bug。这些bug可能只在特定的条件下触发,从而增加了调试和修复的难度。
正确的做法:
如果你希望在派生类中改变基类函数的行为,并且希望这种改变能够通过基类指针或引用来体现,那么你应该将该函数声明为虚函数。
如果你不需要改变基类函数的行为,或者这种改变不需要通过基类指针或引用来体现,那么你可以考虑不在派生类中重新定义该函数,或者通过其他机制(如组合、委托等)来实现你的需求。
总结:在C++中继承基类时,绝不要重新定义继承而来的non-virtual函数。这样做会导致基类的同名函数被遮蔽,从而引发意料之外的行为。如果你需要在派生类中改变基类函数的行为,并且希望这种改变能够通过基类指针或引用来体现,那么应该将该函数声明为虚函数。