Ричард Гиллам в своем «Анатомия оператора присваивания» вероятно, делает неправильное утверждение, когда говорит следующее, только в начале своей статьи:
«Один правильный ответ на этот вопрос будет выглядеть примерно так:»
TFoo&TFoo::operator=(const TFoo& that)
{
if (this != &that)
{
TBar* bar1 = 0;
TBar* bar2 = 0;
try
{
bar1 = new TBar(*that.fBar1);
bar2 = new TBar(*that.fBar2);
}
catch (...)
{
delete bar1;
delete bar2;
throw;
}
TSuperFoo::operator=(that);
delete fBar1;
fBar1 = bar1;
delete fBar2;
fBar2 = bar2;
}
return *this;
}
Я думаю, что автор не прав, потому что если TSuperFoo::operator=()
броски, bar1
а также bar2
будет течь.
Не было бы утечки памяти, если бы это выглядело так:
Tbar* pBar = NULL;
try
{
pBar = new Tbar();
}
catch (...)
{
delete pBar; // clean memory if it was allocated
throw; // error was not handled properly, throw it to caller
}
delete pBar; // no exception was caught, clean the memory
Но на тот случай, если до последнего delete
Есть другой код, который может вызвать исключение, чем вы правы, и действительно существует возможный путь, который приводит к утечке памяти, поскольку в таком случае выделенная память никогда не будет очищена.
Просто печально, что люди пишут код, который не использует замечательные функции, которые предоставляет этот язык, чтобы избежать такого уродливого управления памятью. Обычно достаточно объектов с автоматическим хранением, и вы будете следовать RAII идиома, или в ситуациях, когда динамическое выделение необходимо, тогда хорошей идеей будет обернуть эти голые указатели некоторыми объектами … умные указатели очень помогают.
Других решений пока нет …