Выбрасывает исключение из деструктора для vtable?
Пожалуйста, рассмотрите следующий пример:
#include <csignal>
class A
{
public:
virtual ~A() {}
virtual void foo() = 0;
};
class B : public A
{
public:
virtual ~B() { throw 5; }
virtual void foo() {}
};
int main(int, char * [])
{
A * b = new B();
try
{
delete b;
}
catch ( ... )
{
raise(SIGTRAP);
}
return 0;
}
Я всегда думал (наивно), что, когда программа попадает в этом случае, в раздел catch
, тогда объект B
, в котором точки B
будут неповрежденными, поскольку вполне логично, что исключение будет иметь "отменено" (если запрограммировано безопасно) эффект деструктора. Но когда я попытался запустить этот фрагмент в gdb и попал в точку останова в разделе catch
, я увидел, что объект B ушел, и остался только базовый объект, потому что vtable выглядел так:
(gdb) i vtbl b
vtable for 'A' @ 0x400cf0 (subobject @ 0x603010):
[0]: 0x0
[1]: 0x0
[2]: 0x4008e0 <[email protected]>
Мой вопрос: есть ли способ избежать (половинного) разрушения vtable, если я страстно хочу выбросить исключение из деструктора?
Ответы
Ответ 1
Я всегда думал (наивно), что, когда программа попадает в этом случае, в секцию catch, тогда объект B, в котором b points будет неповрежденным, потому что вполне логично, что исключение будет "отменено" (если запрограммировано безопасно) эффект деструктора.
Это неверно. В стандарте говорится:
Объект любой продолжительности хранения, инициализация или уничтожение которого прекращается с помощью исключения, будет имеют деструкторы, выполняемые для всех своих полностью построенных подобъектов (исключая варианты членов класс объединения), т.е. для подобъектов, для которых главный конструктор (12.6.2) завершил выполнение и деструктор еще не начал выполнение.
(15.2/2 в N4140)
и, возможно, более важно:
Время жизни объекта типа T заканчивается, когда:
- если T - тип класса с нетривиальным деструктором (12.4), вызов деструктора начинается с
(3.8/1.3 в N4140)
Поскольку каждый член и основание b
полностью построен, а не их деструкторов, где они введены, они будут считаться уничтоженными. Итак, в вашем блоке catch
весь объект b
указывает на то, что он уже мертв.
Рациональное позади этого, вероятно, запрещает "частично разрушенные" объекты, так как непонятно, каким должно быть состояние объекта, который не может быть уничтожен. Например, что, если только некоторые из членов, где они уже были уничтожены?
Даже сам стандарт рекомендует против исключений, оставляющих деструкторов. Как я писал ранее в комментарии, бросание деструкторов странно.
Одно хорошее правило, которое мы можем взять из приведенной выше цитаты: объект начинает существовать, когда его конструктор сделан без металирования, и он перестает существовать, как только начинается его деструктор сильное выполнение, независимо от того, как он выходит. (Это уточняется в других местах в стандарте. Есть исключения из этого, они не заботятся о них.)
Итак, в заключение:
Есть ли способ избежать (half-) уничтожения vtable, если я страстно хочу выбросить исключение из деструктора?
Нет. Как только вы введете деструктор, ваш объект будет выполнен для.
Ответ 2
когда программа попадает в этот случае, в секцию улова, тогда объект B, в котором b точек будет неповрежденным, поскольку вполне логично, что исключение будет иметь "отменено" (если запрограммировано безопасно) эффект деструктора.
Нет. время жизни объекта заканчивается, когда его деструктор запускается.
Вы не можете отменить деструктор.
Как говорили другие, бросание деструкторов на С++ странно, и вы хотите избежать их за исключением особых случаев.
Ответ 3
Он хорошо определен и безопасен для броска с деструктора в том, что касается этого экземпляра. Там, где вы начинаете сталкиваться с проблемами, в массивах (потому что он не может завершить удаление массива, и у вас нет способа получить его обратно) и catch clauses (может закончиться завершение вызова). Это также сложно (я думаю, что это на самом деле невозможно, но не готовы утверждать, что из памяти) писать безопасный код исключения, если деструкторы бросают.
Я использовал метание деструкторов, чтобы что-то делать. Например, я работал с API, который мог бы вернуть код ошибки и выделить ошибку blob. Я написал небольшую задачу, которая будет раздавать ссылки для ввода этих данных и проверять деструктор на наличие ошибок. Если он увидит один, он превратит его в исключение и выбросит его.
Такие конструкции технически безопасны, но вы хотите избежать этого, пока не знаете, что делаете. Вы должны четко указать, что эти вещи не могут быть сохранены в векторах или массивах и могут сделать безопасный код исключения безопасным. Основная проблема заключается в том, что почти все ожидают, что все деструкторы будут nothrow.