Инициализация ресурсов, управляемых вручную, может привести к утечке ресурсов, если конструктор выдает исключение на любом этапе.
Сначала рассмотрим этот код с автоматически управляемыми ресурсами:
class Breakfast {
public:
Breakfast()
: spam(new Spam)
, sausage(new Sausage)
, eggs(new Eggs)
{}
~Breakfast() {}
private:
// Automatically managed resources.
boost::shared_ptr<Spam> spam;
boost::shared_ptr<Sausage> sausage;
boost::shared_ptr<Eggs> eggs;
};
Если "new Eggs"
throws, ~Breakfast
не вызывается, но деструкторы всех созданных членов вызываются в обратном порядке, то есть деструкторы sausage
и spam
.
Все ресурсы освобождены должным образом, здесь нет проблем.
Если вы используете необработанные указатели (управляемые вручную):
class Breakfast {
public:
Breakfast()
: spam(new Spam)
, sausage(new Sausage)
, eggs(new Eggs)
{}
~Breakfast() {
delete eggs;
delete sausage;
delete spam;
}
private:
// Manually managed resources.
Spam *spam;
Sausage *sausage;
Eggs *eggs;
};
Если "new Eggs"
throws, помните, ~Breakfast
не вызывается, а скорее деструкторы spam
и sausage
(которые ничего не значат в этой причине, потому что у нас есть необработанные указатели в качестве реальных объектов).
Поэтому у вас есть утечка.
Правильный способ переписать приведенный выше код таков:
class Breakfast {
public:
Breakfast()
: spam(NULL)
, sausage(NULL)
, eggs(NULL)
{
try {
spam = new Spam;
sausage = new Sausage;
eggs = new Eggs;
} catch (...) {
Cleanup();
throw;
}
}
~Breakfast() {
Cleanup();
}
private:
void Cleanup() {
// OK to delete NULL pointers.
delete eggs;
delete sausage;
delete spam;
}
// Manually managed resources.
Spam *spam;
Sausage *sausage;
Eggs *eggs;
};
Конечно, вместо этого вы должны предпочесть оборачивать каждый неуправляемый ресурс в отдельный класс RAII, чтобы вы могли управлять им автоматически и группировать их в другие классы.