在抽象构造函数/析构函数中调用纯虚函数是否安全,如果它有一个体?

时间:2013-08-27 03:28:23

标签: c++

如果没有标记为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

2 个答案:

答案 0 :(得分:7)

不,那不安全。当A构造函数(或析构函数)正在执行时,对象的类型为A,尚未(不再是)B对象。对f()的调用将尝试调度到(仍然)纯虚函数并导致未定义的行为。大多数实现都会捕获这个并使用指示已调用纯虚函数的错误消息终止应用程序。


编辑后:

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

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

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

答案 1 :(得分: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。

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