【问题标题】:Virtual function mechanism with reference to virtual constructor虚函数机制参考虚构造函数
【发布时间】:2012-08-29 12:11:54
【问题描述】:

这是在一次采访中问我的问题。

如果Vtable是在编译时创建的,而vptr是在运行时分配给对象的,那么如果我们的类中有虚拟构造函数,为什么编译器会给出编译时错误?

我解释了整个机制。但他更感兴趣的是'为什么编译时错误而不是运行时错误'

我告诉他 C++ 指南是用粉笔写的,所以编译器会在编译时发送错误。

你能告诉我同样的原因吗

【问题讨论】:

  • c++中没有虚拟构造器这种东西
  • 基本上是正确的answer @up.
  • 你可能想解释一下虚拟构造函数对你或面试官意味着什么。

标签: c++ constructor compiler-errors runtime-error vtable


【解决方案1】:

简单回答的棘手问题 - 因为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) 的构造函数结束后才会生效。

这可以在编译时捕获。

【讨论】:

  • 当然这是对的...我试图从字里行间解读他对 virtual constructor 的含义以及整个事情,但要保持简单... +1
  • 我想这只是一个陷阱,顺便说一句,这对于面试问题来说还是不错的。
  • 但问题是为什么会这样?
  • @juanchopanza - 我不确定你是否看过我的最后一次编辑,我刚刚看过 :?我补充说,这可能会在编译时被捕获,这就是它不是运行时错误的原因。
  • @juanchopanza:不。问题是“为什么在编译时被诊断出来?”
【解决方案2】:

为什么编译时错误而不是运行时错误?

运行时发生异常情况时会发生运行时错误。当编译器检测到 C++ 标准不允许特定构造作为有效的 C++ 构造时,会发生编译时错误。
C++ 标准不允许将构造函数标记为virtual。因此,编译器将其检测为违反语言语法规则并标记错误。

关于为什么 C++ 中不允许使用虚拟构造函数。
Bjarne 在他的常见问题页面上回答问题:

虚拟调用是一种在给定部分信息的情况下完成工作的机制。特别是,“虚拟”允许我们调用只知道任何接口而不知道对象的确切类型的函数。要创建一个对象,您需要完整的信息。特别是,您需要知道要创建的确切类型。因此,“对构造函数的调用”不能是虚拟的。

【讨论】:

    【解决方案3】:

    语言的规则不允许这样做,因为拥有虚拟构造函数没有意义。如何调用此构造函数? C++ 中构造某个基类的不同派生实例的常用方法是工厂方法:

    #include <memory>
    
    // the parameters determine the derived type to be instantiated.
    std::unique_ptr<IFoo> fooFactory(some parameters);
    

    注意smart pointer 的选择应由所有权政策规定。此示例使用唯一所有权。

    【讨论】:

    • Eeew(返回)拥有原始指针。返回std::unique_ptr
    • @daknøk 我可以,我会在我的代码中这样做,但返回指针是大多数文献中看到的经典模式。
    • @daknøk 不幸的是,不是每个人都可以使用它。您必须同意这种事情在很长一段时间内一直是语言中的一个大问题(见证auto_ptr)。不幸的是,每个人都可以访问 C++11 编译器并被允许使用它还需要一段时间。
    • 如果你不允许使用它,你应该改变你的工作场所。
    • 澄清一下,我认为这个答案应该只使用智能指针,而不是把它们作为某种狡猾的评论溜进去
    【解决方案4】:

    在 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++ 中的虚拟调度仅取决于实例的类(但如前所述,这对构造函数没有意义,因为对象还不存在,所以你不能取决于它的真实类)。

    【讨论】:

    • Eeew(返回)拥有原始指针。返回std::unique_ptr
    • @juanchopanza 那么你没有使用 C++
    • 我认为这不值得投反对票。没有人说,使用C++03 是(已经)不好的做法。并且原始指针不是“禁止的”。
    • 当前的 C++ 版本是 C++11,因此,除非 OP 另有明确说明,否则在回答问题时应牢记 C++11。这意味着此答案目前正在教授必须纠正的不良做法。
    • @Kiril:那是什么论据? C++ 中有很多不被禁止的东西,但显然被视为“不好的,不应该使用”。这是 C++ 的优势之一,它很少禁止,也很少推动您采用某些编程风格。相反,您应该使用自己的判断。好吧,除非您确切知道自己在做什么,否则我的建议是不要使用原始指针。当然不要教使用它们。
    猜你喜欢
    • 2012-01-28
    • 2010-09-09
    • 1970-01-01
    • 1970-01-01
    • 2012-04-13
    • 2010-10-05
    • 1970-01-01
    相关资源
    最近更新 更多