четверг, 13 августа 2009 г.

Пустые деструкторы и auto_ptr

Думал некоторое время над довольно простой вещью.
Допустим, у нас есть некоторый класс, который объявлен заранее и используется каким-нибудь "умным" указателем:

class B; // forward declare

class A
{
public:
A();
private:
auto_ptr<B> b;
};

Такое описание мы включаем в header, в cpp соответственно включаем и header, где описан B. Всё правильно, для создания указателя компилятору не нужно видеть описание класса.
Проблема в том, что я не объявил у класса A деструктор. Так как "умные" указатели вызывают для указателя delete в своём деструкторе, нас ждёт проблема: при вызове delete компилятор должен видеть определение класса, чтобы вызвать ему деструктор или перегруженный оператор delete (если определения класса нет - получаем Undefined Behaviour, хотя например g++ показывает warning). Соответственно, если в одном из исходников, куда мы включаем свой header, включён файл с определением B, а в другой - нет, то мы прямо-таки нарываемся на нарушение ODR:
  • Если бы в классе A был объявлен деструктор, то инстанцирование деструктора auto_ptr<B> было бы произведено там, а там у нас есть определение B.
  • Если же деструктора у A нет, то создаётся деструктор по умолчанию, который вызывает деструктор auto_ptr<B>. Но создаётся он во всех файлах, куда включён header с A. Какую потом реализацию выберет компоновщик неизвестно.


Таким образом, опять приходим к прописным истинам:
  • Объявлять пустые деструкторы в такой ситуации однозначно нужно.
  • Или же не выпендриваться и включать все нужные заголовки, а потом пить чай пока проект собирается.
  • Пользоваться boost::scoped_ptr вместо std::auto_ptr, так как в boost проверяется, может ли компилятор удалить объект (и получаем ошибку компиляции в случае неудачи).

Комментариев нет:

Отправить комментарий