【问题标题】:If call to a pure virtual function from destructor is UB, why can we use pure virtual desrtuctors?如果从析构函数调用纯虚函数是UB,为什么我们可以使用纯虚析构函数?
【发布时间】:2014-08-05 10:02:56
【问题描述】:

众所周知,我们可以使用纯虚析构函数,像这样:

struct A {
    virtual ~A() = 0;
};
A::~A() {}

struct B : A {};

因为标准在10.4 [class.abstract] p2中说

纯虚函数只有在使用 (12.4 [class.dtor]) 调用时才需要定义

后来在12.4 [class.dtor] p9

析构函数可以声明为虚拟(10.3)或纯虚拟(10.4);如果在程序中创建了该类或任何派生类的任何对象,则应定义析构函数。

什么意思上面的代码完全有效——A::~A可能是纯虚的,它被定义了,B::~B隐式调用A::~A

到目前为止,一切都很好。
然后我读到10.4 [class.abstract] p6

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

但这正是我们在这里所做的——我们从析构函数中调用纯虚函数A::~A

那么,是不是有些矛盾?

【问题讨论】:

  • 析构函数是一种特殊情况,因为即使它是纯虚拟的,它仍然必须有一个主体(根据标准)。未定义部分适用于纯虚成员函数(可能没有定义)。

标签: c++ destructor undefined-behavior pure-virtual


【解决方案1】:

没有收缩。

你从B的析构函数中调用A的虚拟析构函数。A析构函数不是B的成员。

标准 § 9.3

会员功能

类定义中声明的函数,不包括那些 用友元说明符声明( 11.3 ),被称为该类的成员函数

标准规定当有一个虚拟调用从抽象类析构函数/构造函数(在你的情况下,将是类A)到它自己的纯函数之一时,存在未定义的行为虚成员函数。

[...] 可以从 abstract 类 [...] 的构造函数(或析构函数)调用成员函数;

你的引述是关于析构函数的:

  1. 您可以从抽象类的析构函数中调用成员函数
  2. 如果您同一个抽象类的析构函数调用纯虚方法(通过虚调用),则会出现未定义的行为。

【讨论】:

  • 如果B 是一个抽象类呢?那么它会是UB?
【解决方案2】:

如果有 虚拟调用 (10.3 [class.virtual]),就会发生 UB。

对纯虚函数进行虚调用 (10.3) 的效果是未定义的。

但是 10.3 [class.virtual] p15 这么说

作用域运算符的显式限定 (5.1) 抑制了虚拟调用机制。

似乎隐式析构函数调用具有显式限定。
至少编译器肯定不会在那里进行虚拟调用。

【讨论】:

    猜你喜欢
    • 2021-10-20
    • 2012-07-14
    • 2010-11-16
    • 2012-01-28
    • 2014-02-02
    • 2010-10-12
    相关资源
    最近更新 更多