6

问题

C++ 标准在派生类的析构函数执行之后、基类的析构函数执行之前的时间内保证对象的状态是什么?(这是调用派生类的子对象的析构函数的时候。)

例子

#include <string>
struct Base;

struct Member {
  Member(Base *b);
  ~Member();
  Base *b_;
};

struct Base {
  virtual void f() {}
  virtual ~Base() {}
};

struct Derived : Base {
  Derived() : m(this) {}
  virtual ~Derived() {}  
  virtual void f() {}
  std::string s; 
  Member m;
};

Member::Member(Base *b) : b_(b) {}
Member::~Member() {
  // At this point, ~Derived has finished -- can we use b_ as a 
  // Derived* object (i.e. call Derived::f or access Derived::s)?
  b_->f();
}

int main() {
  Base *bd = new Derived;
  delete bd;
}

在这个例子中,一个Member对象有一个指向Derived拥有它的对象的指针,并且它试图在Derived它被破坏时访问该对象......即使析构函数 forDerived已经完成。

如果某个子对象在执行之后但在执行之前*bd调用了虚函数,则将调用哪个版本的虚函数?在那个状态下访问是否合法?~Derived()~Base()*bd

4

3 回答 3

5

对我来说,从 [12.4] 清楚地表明,这是不合法的:

一旦为对象调用析构函数,该对象就不再存在;如果为生命周期已结束的对象调用析构函数(3.8),则行为未定义。[例子: ...]

尽管没有不再存在的定义,但我认为,我可以说引用不再存在的对象会导致未定义的行为

于 2012-06-27T22:07:54.373 回答
1

在您调用的那一刻,b_->f()正在Derived被破坏,但Base尚未被破坏。不过,您仍然处于不确定状态,因为Derived::f()您将要打电话。

编辑:

成员函数,包括虚函数 (10.3),可以在构造或销毁 (12.6.2) 期间调用。当从构造函数(包括从数据成员的 mem-initializer)或从析构函数直接或间接调用虚函数时,调用应用的对象是正在构造或销毁的对象,调用的函数是在构造函数或析构函数自己的类或其基类之一中定义的函数,但不是在派生自构造函数或析构函数类的类中覆盖它的函数,或在最派生对象的其他基类之一中覆盖它的函数(1.8 )。如果虚函数调用使用显式类成员访问(5.2.

C++0x 工作草案 Sec 12.7 par 4

于 2012-06-27T22:03:10.387 回答
1

来自 n3290 [class.cdtor]

12.7 构造和销毁 1 对于具有非平凡构造函数的对象,在构造函数开始执行之前引用对象的任何非静态成员或基类会导致未定义的行为。对于具有非平凡析构函数的对象,在析构函数完成执行后引用对象的任何非静态成员或基类会导致未定义的行为。

于 2012-06-28T00:30:33.510 回答