C++在构造函数中调用最终的虚函数

Posted

技术标签:

【中文标题】C++在构造函数中调用最终的虚函数【英文标题】:C++ Calling final virtual function in constructor 【发布时间】:2018-11-25 21:40:20 【问题描述】:

在构造函数和析构函数中调用虚函数确实不是一个好习惯,应该避免。这是因为虚函数受子类的影响,但在构造或销毁阶段,子类尚未构造(构造中)或已销毁(销毁中)。

但是,如果在构造函数或析构函数中调用虚拟 final 函数会发生什么?我认为应该没有问题,因为它在逻辑上没有错。

禁止在构造函数和析构函数中调用虚函数,因为在子类中声明的虚函数的重写版本中可能会访问尚未初始化的子类变量。

虽然 virtual final 函数不是,但它是 final 并且无法访问子类的变量。

但这是我的假设,在构造函数或析构函数中调用虚函数不合理可能还有其他原因。

所以,总而言之,

在 C++ 标准中是否允许在构造/销毁阶段调用虚拟 final 函数? 如果是这样,它是否被大多数 C++ 编译器广泛实施? 如果不是,那有什么理由吗?

【问题讨论】:

禁止在构造函数和析构函数中使用虚函数”谁说的? 不确定您是否需要可读的答案或一些冗长的标准引号,所以我没有提供任何标准参考。 (如果你想要带有language-lawyer 的标准引号标签。) 恕我直言,您可能被否决了,因为您的问题将您的假设陈述为事实。也许你应该用“我相信......”来重新表述你的问题。 【参考方案1】:

在构造/销毁阶段调用虚最终函数是 在 C++ 标准中允许吗?

在构造/销毁期间调用虚函数定义明确并且完全合法纯虚函数除外

禁止在构造函数和析构函数中调用虚函数

我不知道(也不在乎)从风格的角度、代码维护的角度来看,谁说它是“不好的”或“禁止的”... 维护代码的能力首先取决于了解相关的语言和工具好;不知道虚拟调用在这些阶段做了什么 (*) 会导致维护人员的误解,通过选择更有经验的维护人员而不是降低编程风格来解决这个问题。

(*) 在技术上不是对象“生命周期”的一部分,这甚至不是一个非常有用的概念,因为对象可以在其构造函数中使用和使用(在它们的生命周期开始之前)以任何非平凡的方式使用程序(我认为标准应该简单地压制这个不需要的概念)。

访问子类的变量,尚未初始化,可以发生在 虚函数的重写版本,在 子类。

不能。在构造基类子对象B(例如由构造函数B::B())期间,正在构造的对象的类型定义为B

虚函数的重写版本,在 子类。

不,此时没有现有的子类对象,所以没有覆盖。

虽然 virtual final 函数不是,但它是 final 并且没有办法 访问子类的变量。

没什么区别。

多态对象的动态类型由构造函数在基类的构造函数之后和构造成员之前建立。

如果是,它是否被大多数 C++ 编译器广泛实现?

实际上,所有编译器通过更改一个或多个 vtable 指针以指向该类型的适当 vtable 来实现设置对象的动态类型;这是作为施工的一部分完成的。

表示在构造过程中,vptr值随着派生对象的构造而变化。

【讨论】:

从构造函数调用虚函数被认为是一种不好的做法。大多数现代 linter 会将此类调用标记为警告。假设您有一个类 A,其中包含从 A 的构造函数调用的虚函数 A:virtual。然后你有一个派生类 B 覆盖该函数。您是正确的,它不会调用派生类版本。但是,您现在已经创建了一种情况,其中虚函数被覆盖,但被覆盖的版本没有被调用。这是有效的代码,但这是一个愚蠢的想法,会在以后调试你的烂摊子时引起很多人的痛苦。 看我的回答。我建议您删除不正确的答案。【参考方案2】:

首先,规则是:“不要直接或间接地从构造函数或析构函数中调用虚函数,该构造函数或析构函数试图调用正在构造或销毁的对象。”那不是意见。这就是 SEI CERT 编码标准。原始文件在:

https://resources.sei.cmu.edu/downloads/secure-coding/assets/sei-cert-cpp-coding-standard-2016-v01.pdf

以及相关规则 OOP50-CPP 的链接:

https://wiki.sei.cmu.edu/confluence/display/cplusplus/OOP50-CPP.+Do+not+invoke+virtual+functions+from+constructors+or+destructors.

在回答最初的问题时,这条规则有几个例外。其中之一,OOP50-CPP-EX2,是函数或类是否被标记为 final。然后它不能被派生类覆盖。您还可以显式限定函数调用。

是的,final 已被广泛实施。

【讨论】:

以上是关于C++在构造函数中调用最终的虚函数的主要内容,如果未能解决你的问题,请参考以下文章

C++中,子类会继承父类的虚函数表!对于父类的析构函数(虚函数) 也会继承吗?

c++中的虚函数有啥作用?

关于C++的虚函数在父类的内部调用

构造函数和析构函数中的虚函数调用[重复]

c++ 中如何调用基类的虚函数?

c++中,虚函数能不能被继承