Устранить возможную утечку памяти при передаче объекта в функцию

Рассмотрим случай, когда функция foo (MyClass * mc) должна сохранять копию mc во внутренней структуре данных и гарантировать, что объект будет удален, когда он больше не используется.

void foo(MyClass* mc) // acquires ownership of mc; may throw
{
// code that may throw
bar(mc); // acquires mc; may also throw
}

Проблема возникает, когда эта функция выполняет код, который может выдать (например, исключение OutOfMemory). Если исключение возникает до того, как указатель был сохранен в структуре данных, объект, очевидно, должен быть освобожден до того, как функция раскручивается, так как вызывающая сторона больше не отвечает за нее (вызывающая сторона даже не знает, был ли указатель фактически сохранен в структура данных или нет).

Можно использовать RAII, чтобы справиться с этим с помощью защитных ограждений, но это кажется очень неуклюжим и создает некоторые накладные расходы (это должно быть сделано в каждой отдельной функции, которая получает указатель).

Нужно ли было бы делать это каждый раз, когда динамически распределяемый объект приобретается, или есть более удобный способ сделать это ?!

template <class T>
struct VerifyAcq {
T* ptr;
bool done;
VerifyAcq(T* ptr):ptr(ptr) { done = false; }
~VerifyAcq() {
if (!done) delete ptr;
}
};

void foo(MyClass* mc) // acquires mc; may throw
{
VerifyAcq<MyClass> va(mc);
// code that may throw
bar(mc); // acquires mc; may throw; must implement the same mechanism!
va.done = true;
}
// Note: there might be no public way of "undoing" what bar has done (no rollbak)
// and even if there was, what if it could also throw?...

Вызывающая сторона не может перехватить исключительную ситуацию, чтобы удалить указатель, потому что перед созданием исключения функция могла успешно добавить указатель на структуру данных, и освобождение объекта сделало бы структуру данных несостоятельной (висячий указатель).

3

Решение

Когда я начал читать код, я остановился на этом:

void foo(MyClass* mc) // acquires ownership of mc; may throw

Комментарий — не лучший способ документировать владение. Лучший способ сделать это — использовать систему типов.

 void foo(std::unique_ptr<MyClass> mc) // no comment required

Исправление интерфейсов также приводит к решению проблемы.

(Если в вашей стандартной библиотеке нет unique_ptr, есть альтернативы, например, Эмуляция Говарда Хиннанта в C ++ 03).

4

Другие решения

У вас есть правильная идея при упоминании RAII.

В упомянутом вами сценарии вы указываете, что удаление должно произойти только тогда, когда bar Функция почему-то не смогла сохранить указатель. Общий указатель как boost::shared_ptr очень хорошо работает для этого:

void foo(boost::shared_ptr<MyClass> mc)
{
// code that may throw
bar(mc); // acquires mc; may also throw
}

void bar(boost::shared_ptr<MyClass> mc)
{
// code that may store the shared_ptr somewhere
}

Пока для ресурса есть хотя бы один общий указатель (указатель), он не будет удален. Как только последний общий указатель уничтожен или сброшен, ресурс удаляется.

Например: если bar функция хранит общий указатель, тогда удаление не произойдет, когда foo Функция заканчивается. В качестве альтернативы, если bar функция не может сохранить общий указатель, тогда удаление произойдет, когда foo заканчивается (при условии, что другие общие указатели не активны для ресурса).

1

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector