Effective C++知识点36:绝不要重新定义继承而来的non-virtual 函数

Effective C++知识点36:绝不要重新定义继承而来的non-virtual 函数
最新回答
重拾记忆的爱

2024-03-01 11:01:05

Effective C++知识点36:绝不要重新定义继承而来的non-virtual函数

在C++中,继承是面向对象编程中的一个核心概念,它允许我们基于已有的类(基类或父类)来构建新的类(派生类或子类)。然而,在继承体系中重新定义基类中的成员函数时,需要特别小心,尤其是对于那些非虚函数(non-virtual函数)。

答案核心:绝不要重新定义继承而来的non-virtual函数,因为这样做会导致基类的同名函数被“遮蔽”,从而引发意料之外的行为。

详细解释

  1. 遮蔽效应

    当在派生类中重新定义了一个基类的非虚函数时,这个派生类的函数会遮蔽(hide)基类中的同名函数。

    遮蔽意味着,通过派生类的对象或引用调用该函数时,将总是调用派生类中的版本,而基类中的版本则无法被直接访问(除非通过特定的类型转换)。

  2. 静态绑定与动态绑定

    非虚函数(non-virtual函数):这些函数在编译时确定调用哪个版本,称为静态绑定(static binding)或早期绑定(early binding)。

    即使你有一个基类指针或引用,指向一个派生类对象,并通过这个指针或引用调用非虚函数,调用的仍然是基类中的版本(如果基类中有这个函数的定义)。

    虚函数(virtual函数):这些函数在运行时确定调用哪个版本,称为动态绑定(dynamic binding)或晚期绑定(late binding)。

    通过基类指针或引用调用虚函数时,会根据指针或引用实际指向的对象类型来确定调用哪个版本的函数。

  3. 为什么不要重新定义继承而来的non-virtual函数

    预期行为与实际行为不符:如果你期望通过基类指针或引用来调用派生类中重定义的函数,但由于该函数是非虚的,你实际上会调用到基类中的版本。这会导致程序的行为与你的预期不符。

    代码可读性和可维护性降低:重新定义非虚函数会使代码的逻辑变得更加复杂和难以理解。其他开发者在阅读你的代码时,可能会因为不知道存在这样的遮蔽效应而陷入困惑。

    潜在的bug:由于静态绑定的特性,重新定义非虚函数很容易引入难以察觉的bug。这些bug可能只在特定的条件下触发,从而增加了调试和修复的难度。

  4. 正确的做法

    如果你希望在派生类中改变基类函数的行为,并且希望这种改变能够通过基类指针或引用来体现,那么你应该将该函数声明为虚函数。

    如果你不需要改变基类函数的行为,或者这种改变不需要通过基类指针或引用来体现,那么你可以考虑不在派生类中重新定义该函数,或者通过其他机制(如组合、委托等)来实现你的需求。

总结:在C++中继承基类时,绝不要重新定义继承而来的non-virtual函数。这样做会导致基类的同名函数被遮蔽,从而引发意料之外的行为。如果你需要在派生类中改变基类函数的行为,并且希望这种改变能够通过基类指针或引用来体现,那么应该将该函数声明为虚函数。