Читаем Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ полностью

static void operator delete(void *pMemory, // обычная

std:size_t size) throw; // форма delete

// уровня класса

...

};


Такое решение наверняка приведет к ошибкам, но чтобы понять, почему это так, придется познакомиться с некоторыми терминами.

Функция operator new, принимающая дополнительные параметры (помимо обязательного аргумента size_t), называется оператором new с размещением или размещающим оператором new (placement new). Приведенный выше оператор new как раз и является таковым. Особенно полезным бывает размещающий оператор new, для которого вторым аргументом служит указатель на область памяти, где объект должен быть сконструирован. Этот оператор new выглядит так:


void *operator new(std::size_t, void *pMemory) throw; // “размещающий new”


Эта версия new является частью стандартной библиотеки C++, и вы получаете к ней доступ, включая в исходный текст директиву #include new. Кстати говоря, такой оператор new используется в реализации класса vector для создания объектов в выделенной для вектора памяти. Это также первоначальная версия оператора new с размещением; именно она и получила название «placement new». Таким образом, сам термин «размещающий new» перегружен. Обычно, когда говорят о размещающем new, имеют в виду эту конкретную функцию: оператор new, принимающий дополнительный аргумент типа void*. Реже так говорят о любой другой версии new, принимающей дополнительные аргументы. Обычно контекст исключает противоречивые толкования, но важно понимать, что общий термин «размещающий new» означает любую версию new, принимающую дополнительные аргументы, поскольку выражение «размещающий delete» или «delete с размещением» (которое мы сейчас обсудим) происходит от него.

Но вернемся к объявлению класса Widget, которое я не одобрил. Проблема в том, что этот класс открывает возможность утечки памяти. Рассмотрим следующий пользовательский код, который протоколирует информацию о выделении памяти в поток cerr при динамическом создании объектов Widget:


Widget *pw = new (std::cerr) Widget; // вызвать оператор new, передав cerr

// в качестве параметра типа ofstream;

// это ведет к утечке памяти в случае,

// когда конструктор Widget возбуждает

// исключение


Если выделение памяти прошло успешно, но конструктор Widget возбуждает исключение, то исполняющая система отвечает за освобождение той памяти, которую успел выделить оператор new. Исполняющая система понятия не имеет, как работает вызванная версия оператора new, поэтому не может отменить результат операции самостоятельно. Вместо этого исполняющая система ищет версию оператора delete, которая принимает то же количество аргументов того же типа, что и new, и если находит его, то вызывает. В данном случае оператор new принимает дополнительный аргумент типа ostream, поэтому соответствующий оператор delete должен иметь следующую сигнатуру:


void operator delete(void *, std::ostream) throw;


По аналогии с размещающими версиями new версии оператора delete, которые принимают дополнительные параметры, называются размещающими delete. Но в классе Widget не объявлена размещающая версия оператора delete, поэтому исполняющая система не знает, как отменить то, что сделал размещающий new. В результате она не делает ничего. В этом примере никакой оператор delete не вызывается, если конструктор Widget возбуждает исключение!

Правило простое: если оператору new с дополнительными аргументами не соответствует оператор delete с такими же аргументами, то никакой delete не вызывается в случае необходимости отменить выделение памяти, выполненное new. Чтобы избежать утечек памяти в приведенном выше коде, Widget должен объявить размещающий оператор delete, который соответствует размещающему оператору new, который выполняет протоколирование:


class Widget {

public:

...

static void *operator new(std:size_t size, std::ostream logStream)

throw(std::bad_alloc);

static void operator delete(void *pMemory) throw;

static void operator delete(void *pMemory, std::ostream logStream)

throw;

...

};


С этим изменением, если конструктор Widget возбудит исключение в предложении


Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже