Во время моего механизма изучения исключений я обнаружил, что есть вызовы деструкторов для полей объекта при разматывании стека. Позвольте мне объяснить в явном виде:
class X
{
File_ptr aa;
Lock_ptr bb;
public:
X(const char* x,const char* y):aa(x),bb(y){}
//.......
}
Итак, теперь, если конструктор Lock_ptr выбрасывает исключение, объект аа будет уничтожен;
Вопрос «почему»? Я всегда думал, что поля объекта не являются обычными автоматическими (lochal) объектами. Они создаются до того, как конструктор их инициализирует. Поэтому их нельзя уничтожить после того, как они выйдут из области видимости конструктора (в противном случае они будут уничтожены, как только конструктор закончил свою работу)
Субобъекты (включая нестатические элементы данных) имеют ту же продолжительность хранения, что и полные объекты, к которым они принадлежат. Это не то же самое, что сказать, что они автоматические. Автоматический объект уничтожается в конце блока. Субобъект уничтожается всякий раз, когда уничтожается его полный объект. Например, если полный объект создан с new
и уничтожен с delete
(т. е. имеет динамическую продолжительность хранения), тогда подобъект также создается в вызове new
и уничтожен в призыве к delete
, С другой стороны, подобъект автоматического объекта также является автоматическим.
Если конструктор для X::bb
выдает исключение, тогда это означает полный объект типа X
не может быть построено. Все подобъекты, которые уже были построены, такие как X::aa
, должен быть уничтожен, потому что подобъект, имеющий ту же продолжительность хранения, что и его завершенный объект, не может выжить без завершенного объекта.
С другой стороны, если строительство всего X
объект успешно завершен, X::aa
и другие подобъекты не будут уничтожены до (вскоре после) полного X
Объект уничтожен.
Правила построения и уничтожения для C ++ предназначены для того, чтобы гарантировать, что при нормальном завершении программы каждый созданный объект также уничтожается ровно один раз. Это важно для идиомы RAII. В этом примере, если X::aa
Получая ресурсы, когда он создан, язык должен гарантировать, что эти ресурсы будут освобождены. Если X::aa
деструктор не вызывается при построении X
не получается, тогда когда это надо называть?
X имеет «настоящий» конструктор, который создает aa
а также bb
, а затем после этого вызывается тело конструктора X. Вот почему список инициализаторов перед X
конструктор «тело». Внутри этого «реального» конструктора базы и затем члены создаются так, как если бы они были в стеке в том порядке, в котором они объявлены в классе, в том числе для разматывания стека. У меня даже есть картинка:
Деструкторы работают по примерно схожему принципу, за исключением обратного, но могут быть еще более сложными, если деструктор является виртуальным. Если деструктор виртуальный, то есть три части. Есть деструктор «заглушки», который вызывается, когда кто-то вызывает деструктор, который отправляет «реальному» деструктору самого производного типа. «Настоящий» деструктор называет ваш деструктор «телом», а затем уничтожает членов в обратном порядке, в котором они объявлены в классе, а затем разрушает базовые классы (все еще в обратном порядке, так же, как стек).
* QuestionC правильно отмечает, что статические члены полностью независимы от всего, что я здесь написал.
Объекты-члены (если они не static
) создаются при вызове конструктора класса. Поведение, которое вы видите, является нормальным и правильным.
Объекты-члены не уничтожаются, когда конструктор завершается. Они уничтожаются, когда вызывается деструктор класса (в обратном порядке построения).