Состояние объекта между вызовом ~Derived() и ~Base()

Вопрос

Что стандарт 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 уже закончил.

Какая версия *bdвиртуальные функции будут вызваны, если какой-то подобъект вызвал виртуальную функцию после ~Derived() выполняется, но до того ~Base() выполняет? Это даже законно, чтобы получить доступ *bd когда это в таком состоянии?

3 ответа

Решение

Из n3290 [class.cdtor]

12.7 Построение и уничтожение 1 Для объекта с нетривиальным конструктором, ссылка на любой нестатический член или базовый класс объекта до того, как конструктор начнет выполнение, приводит к неопределенному поведению. Для объекта с нетривиальным деструктором обращение к любому нестатическому члену или базовому классу объекта после того, как деструктор завершает выполнение, приводит к неопределенному поведению.

Для меня из [12.4] ясно, что это не законно:

Как только деструктор вызывается для объекта, объект больше не существует; поведение не определено, если деструктор вызывается для объекта, время жизни которого истекло (3.8). [Пример: ...]

Несмотря на отсутствие определения, больше не существует, я думаю, я могу сказать, что ссылка на объект, который больше не существует, приводит к неопределенному поведению.

В тот момент, когда вы звоните b_->f(), Derived находится в процессе разрушения, но Base еще не разрушен. Вы все еще в подвешенном состоянии, потому что это Derived::f() что ты будешь звонить.

Редактировать:

Функции-члены, включая виртуальные функции (10.3), могут вызываться во время создания или уничтожения (12.6.2). Когда виртуальная функция вызывается прямо или косвенно из конструктора (в том числе из mem-initializer для элемента данных) или из деструктора, и объект, к которому применяется вызов, является объектом, находящимся в процессе создания или уничтожения, вызываемая функция является один определен в собственном классе конструктора или деструктора или в одной из его баз, но не является функцией, переопределяющей его в классе, производном от класса конструктора или деструктора, или переопределяющим его в одном из других базовых классов наиболее производного объекта (1.8). Если вызов виртуальной функции использует явный доступ к члену класса (5.2.5), а выражение объекта ссылается на объект, находящийся в процессе создания или уничтожения, но его тип не является ни собственным классом конструктора, ни деструктора, ни одной из его баз, результат звонок не определен.

Рабочий проект C++0x, раздел 12.7, п. 4

Другие вопросы по тегам