3

如果没有标有 BODY 的线,我知道这是不安全的。但是有了它,这安全吗?

struct A
{
    virtual ~A() { f(); }

    virtual void f() = 0;
};

void A::f() {} // BODY

struct B : A
{
    void f() {}
};

int main()
{
    delete new B;
}

工作示例:http: //ideone.com/9bRZ3i

4

2 回答 2

7

不,那不安全。当A构造函数(或析构函数)正在执行时,对象是 type A,还不是(不再是)B对象。调用f()将尝试分派到(仍然)纯虚函数并导致未定义的行为。大多数实现都会捕捉到这一点并终止应用程序,并显示一条错误消息,表明调用了纯虚函数。


编辑后:

有一个纯虚函数定义的事实意味着在不经过 virtual dispatch的情况下调用它是合法的。使用动态调度调用纯虚函数仍然是非法的。但是您可以将构造函数重写为:

A::~A() { A::f(); }  // qualification disables dynamic dispatch

如果没有动态调度,代码就会变得有效。

于 2013-08-27T03:38:18.477 回答
4

如果你想绕过虚拟调度并调用你定义的函数体,你必须限定函数名:

virtual ~A() { A::f(); } // OK.

否则,调用将启动虚拟分派,但仅限于基类,因为派生类型的对象在其基类之前已经被销毁。

C++11 §12.7/4 直接解决了您的问题:

成员函数,包括虚函数 (10.3),可以在构造或销毁 (12.6.2) 期间调用。当从构造函数或析构函数直接或间接调用虚函数时,包括在类的非静态数据成员的构造或销毁期间,并且调用所应用的对象是正在构造的对象(称为 x)或破坏,调用的函数是构造函数或析构函数类中的最终覆盖者,而不是在派生更多的类中覆盖它。如果虚函数调用使用显式类成员访问 (5.2.5) 并且对象表达式引用 x 的完整对象或该对象的基类子对象之一,但不是 x 或其基类子对象之一,则行为未定义.

但是,§10.4/6 禁止使用纯虚函数执行此操作:

成员函数可以从抽象类的构造函数(或析构函数)中调用;对于从这样的构造函数(或析构函数)创建(或销毁)的对象,直接或间接地对纯虚函数进行虚调用(10.3)的效​​果是未定义的。

所以,它是UB。

“纯虚拟”的作用是对虚拟查找隐藏函数定义。您永远不会从动态调度函数调用中获得纯虚函数的定义,除非可能是未定义行为的影响。

于 2013-08-27T03:49:50.227 回答