Предотвращение утечек памяти с исключениями в C ++ - PullRequest
1 голос
/ 17 октября 2010

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

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

Имеет ли смысл 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;
}

Ответы [ 5 ]

2 голосов
/ 17 октября 2010

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

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

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

1 голос
/ 17 октября 2010

В этом ответе предполагается, что вы имели в виду 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 также очень широкий молот, который, тем не менее, будет работать для этой проблемы. Я уверен, что приведенные выше примеры не совсем подходят для вашей ситуации, но, надеюсь, они дают вам идеи, которые подходят для вашей проблемы.

1 голос
/ 17 октября 2010

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

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

но что если я захочу использовать f1 и f2 после звонка в комбинат?

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

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.

1 голос
/ 17 октября 2010

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

0 голосов
/ 17 октября 2010

Я изменил 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 );
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...