2010-10-17 2 views
1

Я выделяю объекты в куче, и при некоторых обстоятельствах я объединять их в новый объект (мой класс Foo содержит в основном 2-3 контейнера STL). (Альтернативой было бы использование копий, но я думаю, что это было бы менее эффективно.)Устранение утечек памяти с исключениями на C++

Эти операции могут быть неудачными, поэтому могут быть выбраны исключения. Если я не освобожу динамическую память всякий раз, когда я выдаю исключение, у меня будут утечки памяти.

Будет ли auto_ptr/unique_ptr иметь смысл в этом случае? Я так думаю, так как Combine - это «раковина», но что, если бы я хотел использовать f1 и f2 после вызова Combine?

Спасибо!

Вот мой код:

Foo* MakeFoo() 
{ 
    Foo* foo = 0; 

    Foo* f1 = SimpleFoo(); 
    if(f1) 
    { 
     Foo* f2 = SimpleFoo(); 
     if(f2) 
     { 
      Foo* combined = Combine(f1, f2); 
      delete f2; 
      delete f1; 
     } 
     else 
      foo = f1; 
    } 
    delete foo; 
} 

Foo* SimpleFoo() 
{ 
    Foo* f = 0; 

    if(something) 
    { 
     f = new Foo; 
     if(somethingElse) 
      throw std::runtime_error("Error"); // Memory leak 
    } 

    return f; 
} 

Foo* Combine(const Foo* f1, const Foo* f2) 
{ 
    assert(f1); 
    assert(f2); 

    // Memory leak in MakeFoo() 
    if(something) 
     throw std::runtime_error("Error"); 

    Foo* foo = new Foo; 

    // Maybe one could also simply modify f1 
    foo->Add(*f1); 
    foo->Add(*f2); 

    return foo; 
} 
+0

Не могли бы вы немного убрать код? Что такое 'что-то 'и' somethingElse'? Где вы ловите исключения? –

+0

что-то и что-тоElse - это всего лишь некоторые условия (код является частью очень простого рекурсивного спуска парсера), исключения попадают в основную. – Andreas

ответ

1

Этот ответ предполагает, что вы имели в виду return foo в конце MakeFoo().Мой первый выбор был бы реорганизовать таким образом, чтобы не использовать столько динамическое распределение, вдоль линий этого:

Foo *MakeFoo(){ 
    if(!something) 
    return 0; 

    return Combine(SimpleFoo(), SimpleFoo()); 
} 

Foo SimpleFoo(){ 
    Foo foo; 
    if (something2) // hopefully related to foo. Otherwise, put this condition in MakeFoo 
    throw std::runtime_error("Error"); 

    return foo; 
} 

Foo *Combine(const Foo &f1, const Foo &f2){ 
    if (something3) 
    throw std::runtime_error("Error"); 

    Foo *combination = new Foo; 
    combination->add(f1); 
    combination->add(f2); 

    return combination; 
} 

Если это не вариант для вас, я хотел бы написать что-то вроде этого в '03:

Foo *MakeFoo(){ 
    auto_ptr<Foo> f1 (SimpleFoo()); 
    if (!f1.get()) 
    return 0; 

    auto_ptr<Foo> f2> (SimpleFoo()); 
    if (!f2.get()) 
    return f1.release(); 

    Foo *combined = Combine(f1.get(), f2.get()); 
    f1.release(); 
    f2.release(); 
    return combined; 
} 

Foo *SimpleFoo(){ 
    if (!something) 
    return 0; 

    auto_ptr<Foo> f (new Foo); 
    if (somethingHopefullyRelatedToF) 
    throw std::runtime_error("Error"); 

    return f.release(); 
} 

unique_ptr может использоваться одинаково. shared_ptr - также очень широкий молот, который, тем не менее, будет работать для этой проблемы. Я уверен, что приведенные выше примеры не подходят для вас, но, надеюсь, они дают вам идеи, которые применимы к вашей проблеме.

+0

Спасибо, это выглядит очень хорошо! – Andreas

1

Да, unique_ptr имеет смысл здесь. Он обрабатывает управление памятью, a la RAII. Если вы хотите использовать два объекта после того, как вызов будет объединен, он будет работать, поскольку оба они инициализируются перед вызовом и создаются за пределами области вызова Combine.

+0

Я в этом сомневаюсь. То, как я понял unique_ptr, эти умные указатели уникальны, т. Е. Combine получит право собственности на указатели и освободит память после ее возвращения. – Andreas

+0

@ Андреас хорошая точка. shared_ptr был бы лучшим вариантом, но, похоже, я был избит до удара. – wheaties

1

Будет ли auto_ptr/unique_ptr иметь смысл в этом случае?

Да, auto_ptr может использоваться. Это гарантирует, что утечек памяти не будет.

но что, если бы я хотел использовать f1 и f2 после вызова Combine?

Вы можете передать исходные указатели для комбинирования функций. Пример кода может быть следующим.

auto_ptr<Foo> f1 = auto_ptr<Foo>(new Foo); 
auto_ptr<Foo> f2 = auto_ptr<Foo>(new Foo); 
Foo* combined = Combine(f1.get(), f2.get()); 

Таким образом, владение указателями не будет передано для объединения функции. Итак, вы можете использовать f1 и f2 после функции комбинирования.

Кроме того, убедитесь, что вы добавили улов в функцию MakeFoo, чтобы уловить исключения, вызванные функцией Combine.

+0

Позвонив get(), вы не передаете право собственности на объединение. Вы должны сменить комбинацию, чтобы использовать автоматические указатели и передать право собственности на комбинат. –

+0

@Martin: То, что я имел в виду :). Поскольку OP хочет использовать указатели после вызова для объединения, я передаю исходные указатели, используя get(). Он не передаст права собственности. Если передан автоматический указатель, тогда функция объединения получит право собственности, поэтому OP не сможет использовать указатели после вызова объединения, поскольку они будут освобождены внутри комбайна. – bjskishore123

2

unique_ptr может помочь здесь с управлением памятью, но если у вас нет поддержки перемещения, встроенной в ваш контейнер в Foo :: Add, вы захотите использовать shared_ptr вместо этого, потому что вы не можете скопировать содержимое уникального_объекта unique_ptr из этого.

Если у вашего stl есть unique_ptr и shared_ptr, вероятно, вы не должны использовать auto_ptr.

typedef shared_ptr также очистит код.

+0

Спасибо, shared_ptr звучит неплохо. – Andreas

+0

Кстати, а как насчет производительности? – Andreas

+0

shared_ptr звучит намного более гибко, чем auto_ptr, кажется соблазнительным всегда использовать его ... Каковы недостатки? – Andreas

0

Я модифицировал Combine и выбрал комбинацию ваших анверов, чтобы решить эту проблему. Код намного короче и легче читать. Я очень доволен этим, спасибо всем!

typedef std::auto_ptr<Foo> A_Foo; 

A_Foo MakeFoo() 
{ 
    A_Foo foo = SimpleFoo(); 
    if(foo.get()) 
    { 
     A_Foo f2 = SimpleFoo(); 
     if(f2.get()) 
      Combine(*foo, *f2); 
    } 
    return foo; 
} 

A_Foo SimpleFoo() 
{ 
    if(something) 
    { 
     A_Foo f(new Foo); 
     if(somethingElse) 
      throw std::runtime_error("Error"); 
     return f; 
    } 
    else 
     return A_Foo(); 
} 

void Combine(Foo& f1, const Foo& f2) 
{ 
    if(something) 
     throw std::runtime_error("Error"); 

    f1.Add(f2); 
} 
Смежные вопросы