避免内存泄漏与C ++中的异常

时间:2010-10-17 16:43:11

标签: c++ stl

我在堆上分配对象,在某些情况下我将它们组合成一个新对象(我的Foo类主要包含2-3个STL容器)。 (另一种方法是使用副本,但我想这样效率会降低。)

这些操作可能会失败,因此可能会抛出异常。 除非我在抛出异常时释放动态内存,否则我将发生内存泄漏。

在这种情况下auto_ptr / unique_ptr会有意义吗? 我猜是因为Combine是一个“接收器”,但如果我想在调用Combine后使用f1和f2怎么办?

谢谢!

这是我的代码:

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 个答案:

答案 0 :(得分:2)

unique_ptr可以帮助你进行内存管理,但是除非你在Foo :: Add中将容器内置移动支持,否则你会想要使用shared_ptr,因为你无法复制unique_ptr的内容只移动所有权它

如果你的stl有unique_ptr和shared_ptr,你可能不应该使用auto_ptr。

shared_ptr的typedef也会清除一些代码。

答案 1 :(得分:1)

是的,unique_ptr在这里有意义。它为您处理内存管理,RAII。如果你想在调用之后使用这两个对象,它将在调用之前初始化并在调用Combine的范围之外创建。

答案 2 :(得分: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函数中添加一个catch来捕获Combine函数抛出的异常。

答案 3 :(得分: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也是一个非常广泛的锤子,但仍然可以解决这个问题。我确信上面的例子并不适合你的情况,但希望他们能给你适合你问题的想法。

答案 4 :(得分: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 );
}