Давайте поговорим о неприятных граблях связанных с удалением неполного типа, что может привести к крайне неприятным последствиям, например милому сердцу Segmentation fault. Связано это с двумя вещами:
- Полномочиями данными компилятору автоматически генерировать деструктор, если он не определен в классе
- Возможностью вызвать оператор delete для объекта тип которого в точке удаления еще не известен
Рассмотрим простой код и посмотрим, что делает компилятор:
test.h
#ifndef TEST_H #define TEST_H class Test { public: Test(); ~Test(); }; #endif//TEST_H |
test.cpp
#include <iostream> #include "test.h" Test::Test() { std::cout << "Test" << std::endl; } Test::~Test() { std::cout << "~Test" << std::endl; } |
trouble.h
#ifndef TROUBLE_H #define TROUBLE_H #include <memory> class Test; class Trouble { public: Trouble(); private: std::auto_ptr<Test> Test_; }; #endif//TROUBLE_H |
trouble.cpp
#include "test.h" #include "trouble.h" Trouble::Trouble() : Test_(new Test()) { } |
main.cpp
#include "trouble.h" int main(int argc, char* argv[]) { Trouble trouble; return 0; } |
Итак, имеется два класса: Test и Trouble, причем в целях ускорения компиляции разработчик решил сделать предварительное объявление класса Test в trouble.h, но не написал деструктор для класса Trouble, а значит компилятор заботливо создаст деструктор сам. Когда он это сделает? Вообще компилятор не мечется по коду, а последовательно его анализирует.
Начнет он с функции main в main.cpp, определит, что создается объект trouble типа Trouble из trouble.h, обнаружит конструктор.
Далее он сделает вывод, что по выходу из main объект trouble должен быть удален, попытается найти деструктор, не найдет его и создаст его сам.
Что будет в этом деструкторе? Естественно удаление членов класса, то есть в данном случае std::auto_ptr<Test>, в деструкторе которого будет соответственно вызван оператор delete для указателя на Test.
Внимательно следим за руками! В данном месте компилятор еще ничего не знает о типе Test, поскольку еще не дошел до test.h и поэтому оператор delete будет применен к неполному типу, что вызовет неопределенное поведение.
Откомпилировав приведенный код и выполнив программу вы скорее всего на выводе получите только сообщение из конструктора, деструктор для Test вызван не будет!
Далее подробности и методы борьбы с данным явлением.