这是在一次采访中向我提出的问题。
如果 Vtable 是在编译时创建的,并vptr
在运行时分配给对象,那么如果我们的类中有虚拟构造函数,为什么编译器会给出编译时错误?
我解释了整个机制。但他对“为什么编译时错误而不是运行时错误”更感兴趣
我告诉他 C++ 指南是这样写的,所以编译器会在编译时发送错误。
你能告诉我同样的原因吗
这是在一次采访中向我提出的问题。
如果 Vtable 是在编译时创建的,并vptr
在运行时分配给对象,那么如果我们的类中有虚拟构造函数,为什么编译器会给出编译时错误?
我解释了整个机制。但他对“为什么编译时错误而不是运行时错误”更感兴趣
我告诉他 C++ 指南是这样写的,所以编译器会在编译时发送错误。
你能告诉我同样的原因吗
简单回答的棘手问题 - 因为在C++
.
在 ISO 标准中,ISO/IEC 14882:2003 和 ISO/IEC 14882:2011,12.1 构造函数,第 4 点:
构造函数不应是虚拟的(10.3)或静态的(9.4)。可以为 const、volatile 或 const volatile 对象调用构造函数。构造函数不得声明为 const、volatile 或 const volatile (9.3.2)。const 和 volatile 语义(7.1.5.1)不适用于正在构建的对象。此类语义仅在最派生对象 (1.8) 的构造函数结束后才生效。
这可以在编译时捕获。
为什么编译时错误不是运行时错误?
当运行时出现异常情况时,会发生运行时错误。当编译器检测到 C++ 标准不允许特定构造作为有效的 C++ 构造时,会发生编译时错误。
C++ 标准不允许将构造函数标记为virtual
. 因此,编译器将其检测为违反语言语法规则并标记错误。
至于回答为什么 C++ 中不允许使用虚拟构造函数。
Bjarne 在他的常见问题页面上回答 Q:
虚拟调用是一种在给定部分信息的情况下完成工作的机制。特别是,“虚拟”允许我们调用只知道任何接口而不知道对象的确切类型的函数。要创建一个对象,您需要完整的信息。特别是,您需要知道要创建的确切类型。因此,“对构造函数的调用”不能是虚拟的。
语言的规则不允许这样做,因为拥有虚拟构造函数没有意义。如何调用此构造函数?C++ 中构造某个基类的不同派生实例的常用方法是工厂方法:
#include <memory>
// the parameters determine the derived type to be instantiated.
std::unique_ptr<IFoo> fooFactory(some parameters);
注意智能指针的选择应由所有权策略决定。此示例使用唯一所有权。
在 C++ 中,“虚拟”意味着在运行时所做的事情将取决于对象的有效类,而不仅仅取决于变量的类型。
“虚拟”构造函数实际上没有意义,因为您还没有对象(您想构建一个对象),因此您没有可以依赖的类来做出决定。
有时,使用“虚拟构造函数”,C++ 中的意图是一种模式,您可以在不知道确切类的情况下构建对象......例如:
class Document {
public:
static Document *create(...);
private:
Document(...);
};
...
// Just use Document::create instead of new Document
std::unique_ptr<Document> p = Document::create(...);
在这种情况下,类的用户不能调用构造函数(它是私有的),但他们只能调用一个公共的静态方法,该方法将返回一个指向实例的指针。构造本身将由此函数处理,返回的对象不一定是Document
实例,而是派生自Document
您不知道且未公开公开的某个类的实例。例如,这允许在运行时根据环境或调用中指定的参数来决定确切的类create
。
这被称为“虚拟构造函数”,因为被调用的构造函数将在运行时决定。然而,它与 C++ 中的虚拟方法调用不同,因为 C++ 中的虚拟调度仅取决于实例的类(但如前所述,这对构造函数没有意义,因为对象还不存在,所以你不能决定取决于它的真实类)。