简体   繁体   English

以下代码是否会导致c ++中的内存泄漏

[英]Will the below code cause memory leak in c++

class someclass {};

class base
{
    int a;
    int *pint;
    someclass objsomeclass;
    someclass* psomeclass;
public:
    base()
    {
        objsomeclass = someclass();
        psomeclass = new someclass();
        pint = new int(); 
        throw "constructor failed";
        a = 43;
    }
}

int main()
{
    base temp();
}

In the above code, the constructor throws. 在上面的代码中,构造函数抛出。 Which objects will be leaked, and how can the memory leaks be avoided? 哪些对象会泄露,以及如何避免内存泄漏?

int main()
{
    base *temp = new base();
}

How about in the above code? 在上面的代码中怎么样? How can the memory leaks be avoided after the constructor throws? 构造函数抛出后如何避免内存泄漏?

Yes it will leak memory. 是的它会泄漏内存。 When the constructor throws, no destructor will be called (in this case you don't show a destructor that frees the dynamically allocated objects, but lets assume you had one). 当构造函数抛出时,不会调用析构函数(在这种情况下,您不会显示释放动态分配的对象的析构函数,但假设您有一个)。

This is a major reason to use smart pointers - since the smart poitners are full fledged objects, they will get destructors called during the exception's stack unwind and have the opportunity to free the memory. 这是使用智能指针的一个主要原因 - 因为聪明的poitners是完全成熟的对象,它们将在异常的堆栈放松期间调用析构函数,并有机会释放内存。

If you use something like Boost's scoped_ptr<> template, your class could look more like: 如果您使用Boost的scoped_ptr <>模板之类的东西,您的类可能看起来更像:

class base{
    int a;
    scoped_ptr<int> pint;
    someclass objsomeclass;
    scoped_ptr<someclass> psomeclass;
    base() : 
       pint( new int),
       objsomeclass( someclass()),
       psomeclass( new someclass())

    {
        throw "constructor failed";
        a = 43;
    }
}

And you would have no memory leaks (and the default dtor would also clean up the dynamic memory allocations). 而且你没有内存泄漏(默认的dtor也会清理动态内存分配)。


To sum up (and hopefully this also answers the question about the 总结(并希望这也回答了有关的问题

base* temp = new base();

statement): 声明):

When an exception is thrown inside a constructor there are several things that you should take note of in terms of properly handling resource allocations that may have occured in the aborted construction of the object: 当在构造函数中抛出异常时,在正确处理可能在对象的中止构造中发生的资源分配方面,您应该注意以下几点:

  1. the destructor for the object being constructed will not be called. 不会调用正在构造的对象的析构函数。
  2. destructors for member objects contained in that object's class will be called 将调用该对象类中包含的成员对象的析构函数
  3. the memory for the object that was being constructed will be freed. 将释放正在构造的对象的内存。

This means that if your object owns resources, you have 2 methods available to clean up those resources that might have already been acquired when the constructor throws: 这意味着如果您的对象拥有资源,您可以使用两种方法来清理构造函数抛出时可能已经获取的那些资源:

  1. catch the exception, release the resources, then rethrow. 捕获异常,释放资源,然后重新抛出。 This can be difficult to get correct and can become a maintenance problem. 这可能很难纠正并且可能成为维护问题。
  2. use objects to manage the resource lifetimes (RAII) and use those objects as the members. 使用对象来管理资源生命周期(RAII)并将这些对象用作成员。 When the constructor for your object throws an exception, the member objects will have desctructors called and will have an opportunity to free the resource whose lifetimes they are responsible for. 当对象的构造函数抛出异常时,成员对象将调用析构函数,并有机会释放他们负责的生命周期的资源。

Both new's will be leaked. 两个新的都将被泄露。

Assign the address of the heap created objects to named smart pointers so that it will be deleted inside the smart pointers destructor that get call when the exception is thrown - ( RAII ). 将堆创建的对象的地址分配给命名的智能指针,以便在抛出异常时获取调用的智能指针析构函数中删除它 - ( RAII )。

class base {
    int a;
    boost::shared_ptr<int> pint;
    someclass objsomeclass;
    boost::shared_ptr<someclass> psomeclass;

    base() :
        objsomeclass( someclass() ),
        boost::shared_ptr<someclass> psomeclass( new someclass() ),
        boost::shared_ptr<int> pint( new int() )
    {
        throw "constructor failed";
        a = 43;
    }
};

Now psomeclass & pint destructors will be called when the stack unwind when the exception is thrown in the constructor, and those destructors will deallocate the allocated memory. 现在,当构造函数中抛出异常时,当堆栈展开时,将调用psomeclasspint析构函数,并且这些析构函数将释放已分配的内存。

int main(){
    base *temp = new base();
}

For ordinary memory allocation using (non-plcaement) new, memory allocated by the operator new is freed automatically if the constructor throws an exception. 对于使用(非plcaement)new的普通内存分配,如果构造函数抛出异常,则由operator new分配的内存将自动释放。 In terms of why bother freeing individual members (in response to comments to Mike B's answer), the automatic freeing only applies when an exception is thrown in a constructor of an object being new'ly allocated, not in other cases. 就为什么要解放个别成员而言(响应对Mike B的回答的评论),自动释放仅适用于在新分配的对象的构造函数中抛出异常,而不是在其他情况下。 Also, the memory that is freed is those allocated for the object members, not any memory you might have allocated say inside the constructor. 此外,释放的内存是为对象成员分配的内存,而不是您在构造函数中分配的任何内存。 ie It would free the memory for the member variables a , pint , objsomeclass , and psomeclass , but not the memory allocated from new someclass() and new int() . 即它将释放成员变量apintobjsomeclasspsomeclass的内存,但不释放new someclass()new int()分配的内存。

I believe that the top answer is wrong and would still leak memory. 我认为最好的答案是错误的,但仍然会泄漏内存。 The destructor for the class members will not be called if the constructor throws an exception (because it never completed its initialization, and perhaps some members have never reached their constructor calls). 如果构造函数抛出异常(因为它从来没有完成初始化,也许有些成员也从未达到它们的构造函数调用)的类成员的析构函数不会被调用。 Their destructors are only called during the class's destructor call. 他们的析构函数只在类的析构函数调用期间调用。 That only makes sense. 这才有意义。

This simple program demonstrates it. 这个简单的程序演示了它。

#include <stdio.h>


class A
{
    int x;

public:
    A(int x) : x(x) { printf("A constructor [%d]\n", x); }
    ~A() { printf("A destructor [%d]\n", x); }
};


class B
{
    A a1;
    A a2;

public:
    B()
    :   a1(3),
        a2(5)
    {
        printf("B constructor\n");
        throw "failed";
    }
    ~B() { printf("B destructor\n"); }
};


int main()
{
    B b;

    return 0;
}

With the following output (using g++ 4.5.2): 使用以下输出(使用g ++ 4.5.2):

A constructor [3]
A constructor [5]
B constructor
terminate called after throwing an instance of 'char const*'
Aborted

If your constructor fails partway then it is your responsibility to deal with it. 如果你的构造函数失败了,那么你有责任处理它。 Worse, the exception may be thrown from your base class' constructor! 更糟糕的是,可能会从基类的构造函数中抛出异常! The way to deal with these cases is by employing a "function try block" (but even then you must carefully code the destruction of your partially initialized object). 处理这些情况的方法是使用“函数try块”(但即使这样,你也必须仔细编写部分初始化对象的破坏代码)。

The correct approach to your problem would then be something like this: 正确解决问题的方法是这样的:

#include <stdio.h>


class A
{
    int x;

public:
    A(int x) : x(x) { printf("A constructor [%d]\n", x); }
    ~A() { printf("A destructor [%d]\n", x); }
};


class B
{
    A * a1;
    A * a2;

public:
    B()
    try  // <--- Notice this change
    :   a1(NULL),
        a2(NULL)
    {
        printf("B constructor\n");
        a1 = new A(3);
        throw "fail";
        a2 = new A(5);
    }
    catch ( ... ) {   // <--- Notice this change
        printf("B Cleanup\n");
        delete a2;  // It's ok if it's NULL.
        delete a1;  // It's ok if it's NULL.
    }

    ~B() { printf("B destructor\n"); }
};


int main()
{
    B b;

    return 0;
}

If you run it you will get the expected output where only the allocated objects are destroyed and freed. 如果运行它,您将获得预期的输出,其中只有已分配的对象被销毁和释放。

B constructor
A constructor [3]
B Cleanup
A destructor [3]
terminate called after throwing an instance of 'char const*'
Aborted

You can still work it out with smart shared pointers if you want to, with additional copying. 如果需要,您仍然可以使用智能共享指针进行额外复制。 Writing a constructor similar to this: 编写类似于此的构造函数:

class C
{
    std::shared_ptr<someclass> a1;
    std::shared_ptr<someclass> a2;

public:
    C()
    {
        std::shared_ptr<someclass> new_a1(new someclass());
        std::shared_ptr<someclass> new_a2(new someclass());

        // You will reach here only if both allocations succeeded. Exception will free them both since they were allocated as automatic variables on the stack.
        a1 = new_a1;
        a2 = new_a2;
    }
}

Good luck, Tzvi. 祝你好运,Tzvi。

Yes, that code will leak memory. 是的,该代码将泄漏内存。 Blocks of memory allocated using "new" are not freed when an exception is raised. 引发异常时,不会释放使用“new”分配的内存块。 This is part of the motivation behind RAII . 这是RAII背后的动机的一部分。

To avoid the memory leak, try something like this: 为避免内存泄漏,请尝试以下方法:

psomeclass = NULL;
pint = NULL;
/* So on for any pointers you allocate */

try {
    objsomeclass = someclass();
    psomeclass = new someclass();
    pint = new int(); 
    throw "constructor failed";
    a = 43;
 }
 catch (...)
 {
     delete psomeclass;
     delete pint;
     throw;
 }

If you throw in a constructor, you should clean up everything that came before the call to throw. 如果你抛出一个构造函数,你应该清理抛出调用之前的所有内容。 If you are using inheritance or throwing in a destructor, you really shouldn't be. 如果你使用继承或抛出析构函数,你真的不应该。 The behaviour is odd (don't have my standard handy, but it might be undefined?). 行为是奇怪的(没有我的标准方便,但它可能是未定义的?)。

Everything you "new" needs to be deleted, or you'll cause a memory leak. 您需要删除“新”的所有内容,否则会导致内存泄漏。 So these two lines: 所以这两行:

psomeclass = new someclass();
pint = new int(); 

Will cause memory leaks, because you need to do: 会导致内存泄漏,因为你需要这样做:

delete pint;
delete psomeclass;

In a finally block to avoid them being leaked. 在最后一个块中,以避免它们泄露。

Also, this line: 另外,这一行:

base temp = base();

Is unnecessary. 没必要。 You just need to do: 你只需要这样做:

base temp;

Adding the "= base()" is unnecessary. 不需要添加“= base()”。

you need to delete psomeclass... Its not necessary to clean up the integer... 你需要删除psomeclass ...没有必要清理整数...

RWendi RWendi

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM