2010-01-02 288 views
6

我觉得这很奇怪。在Sample_Base的ctor中,我调用了内部调用fun()的bar(),它是一个纯虚函数。我得到了称为“纯虚函数”的错误。这很好。现在,如果我直接从Sample_Base的ctor调用fun(),我不会得到那个错误。我在VC++ 2010 Beta 2和Ubuntu 9.10上的g ++ 4.4.1上尝试过。我同意,除了纯虚拟析构函数之外,为纯虚函数提供实现是没有意义的。但是,我对这种行为有点惊讶。纯虚函数调用error

class Sample_Base 
{ 
public: 
    Sample_Base() 
    { 
     bar(); 
     // fun(); 
    } 
    /* This is code does not throw any error. 
    Sample_Base() 
    { 
     fun(); 
    } 
    */ 

    void bar() 
    { 
     fun(); 
    } 
    virtual void fun() = 0; 
    virtual ~Sample_Base(); 
}; 

Sample_Base::~Sample_Base() 
{ 

} 

void Sample_Base::fun() 
{ 
    std::cout << "Sample_Base::fun\n"; 
} 

class Sample_Derived : public Sample_Base 
{ 
public: 
    Sample_Derived() : Sample_Base() 
    { 
     fun(); 
    } 

    void fun() 
    { 
     std::cout << "Sample_Derived::fun\n"; 
    } 

    ~Sample_Derived() 
    { 

    } 
}; 

回答

6

当您直接调用函数时,由于您在构造函数中,因此编译器会解析对象的静态类型(Sample_Base)并直接调用Sample_Base::fun()。既然你为它提供了一个实现,编译器找到了这个函数并且它可以工作。

当您间接调用它时,通过bar(),编译器必须使用动态类型,因此它会执行虚拟调用,以便在运行时解析。在那里它失败了,因为它调用了纯虚函数。

所以差异在于它将函数绑定到调用。

+0

[C++标准](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf)§10.4.6指出“虚拟调用纯虚函数...正在创建的对象...未定义“。你能评论一下吗?我会读它直接调用'fun'也应该是未定义的(即使编译器在这种特定情况下不会产生崩溃的程序)。或者这不是在这种情况下的虚拟调用(如果是这样,为什么,我无法在标准中找到它)。 – Xlea 2015-04-23 13:13:59

+0

@Xlea Charles Bailey在答案中指出了标准的正确部分。请注意,他当时正在使用C++ 03标准,因此现在章节号会有所不同,但这可能会引导您。 – Gorpik 2015-04-24 07:41:33

1

调用虚函数将不会调用派生类中的重载函数。在构造函数或析构函数中调用纯虚函数是未定义的行为

您可能会感兴趣的阅读thisthis.

1

在施工时间,当Sample_Base构造函数被调用时,对象还没有完全建立。具体来说,属于Sample_Derived的部分尚未创建,将被Sample_Derived覆盖的虚拟功能的调用将不调用Sample_Derived中的实现,而是在Sample_Base中定义的版本。由于该函数没有实现,所以你会得到一个错误。请参阅this entry in the C++ FAQ Lite

+0

我同意,当在Sample_Base中调用bar时,该对象不是完全构造的,但是当我从Sample_Base的构造函数调用fun()时,我不会收到任何错误。这就是我所感兴趣的。 – Jagannath 2010-01-02 10:29:03

+0

@Jagannath:接收或不接收错误不是你应该担心的,你应该担心这个设计是危险的。您应该尝试避免从构造函数/析构函数调用虚拟方法。 – 2010-01-02 10:39:25

+0

@Gal:我知道设计不正确,应该避免从构造函数中调用虚函数。我只是对行为的差异感兴趣。不管怎么说,多谢拉。 – Jagannath 2010-01-02 10:42:34

4

提供纯虚函数的定义并不一定是毫无意义的。标记虚函数纯意味着封闭类是抽象的,并且从它派生的任何类都是抽象的,除非该函数的最终覆盖不是纯虚函数。纯虚拟函数仍然可以通过显式的非虚拟调用来调用。

在基类构造体(而不是从一个构造函数-初始化)虚拟函数的版本通过虚拟呼叫称为是一个在该类中定义本身或其基础之一,而不是任何重写它的类(它不会被构造)。这在12.7 [class.cdtor]/3中明确指定。

在构造函数主体中显式调用纯虚函数是合法的(即使用显式类限定符) - 虽然这需要函数定义一个主体 - 但它是未定义的行为来调用纯虚函数通过只能从抽象类的构造函数或析构函数中进行的虚拟调用。这在10.4 [class.abstract]/6中明确指定。

1

此行为不是未定义的,它是明确定义的:虚拟函数在构造函数和析构函数中不是虚拟的。他们称之为函数的静态版本。如果函数是纯虚函数,则会导致VC中出现着名的“纯虚拟调用”错误。

我在一个多线程程序中看到了一个有趣的变体:线程B正试图调用一个虚函数时,一个对象正在线程A上被破坏。在构造函数或析构函数中没有虚函数调用,但我们仍然遇到纯虚函数调用错误。