调用 ~Derived() 和 ~Base() 之间的对象状态

The state of an object between a call to ~Derived() and ~Base()

本文关键字:对象 状态 之间 Derived 调用 Base      更新时间:2023-10-16

问题

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对象被破坏时访问该对象...即使 Derived 的析构函数已经完成。

如果某个子对象在~Derived()执行之后但在执行~Base()之前调用虚函数,则会调用*bd的虚函数的哪个版本? 当*bd处于该状态时,访问它是否合法?

对我来说

,从 [12.4] 开始,它清楚地表明它是不合法的:

为对象调用析构函数后,该对象将不再 存在;如果为析构函数调用 生存期已结束的对象 (3.8)。[示例: ...]

尽管缺乏不再存在的定义

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

来自 n3290 [class.cdtor]

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

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

编辑:

成员函数,包括虚函数 (10.3),可以调用 在施工或销毁期间(12.6.2)。当虚拟函数 直接或间接从构造函数调用(包括从 数据成员的 mem 初始值设定项)或析构函数,以及 调用适用的对象是正在构造的对象或 破坏,调用的函数是构造函数中定义的函数 或析构函数自己的类或在其基之一中,但不是函数 在派生自构造函数或析构函数的类中重写它 类,或在最 派生对象 (1.8)。如果虚函数调用使用显式 类成员访问 (5.2.5) 和对象表达式引用 正在建造或破坏的对象,但其类型既不是 构造函数或析构函数自己的类或其基之一,结果 的调用未定义。

C++0x 工作草案 Sec 12.7 标准杆 4 杆