【问题标题】:Stack unwinding dynamically created object whose constructor acts also on heap堆栈展开动态创建的对象,其构造函数也作用于堆
【发布时间】:2013-12-13 20:41:40
【问题描述】:

当类的构造函数在堆上分配内存时,例如

class bla
{
    private:
        double *data;
        int size;

    public:
        bla(int s)
        {
            size = s;
            data = new double [size];
        }
        ~bla()
        {
            delete[] data;
        }
}

我有一个功能,例如

void f(bool huge)
{
    bla *ptr;
    if (huge)
        ptr = new bla(1e10);
    else
        ptr = new bla(1e2);

    //...

    delete ptr;
}

如果ptr = new bla(1e10) 的分配成功(这意味着datasize 已分配)但不是构造函数-> 因为1e10 太大而引发异常,会发生什么情况?我没有data = new double [size] 的内存泄漏,但是我是否在堆上仍然存在double *dataint size 的内存泄漏?还是通过堆栈展开清除它们?

我应该这样写更好吗?:

void f(bool huge)
{
    bla *ptr = 0;
    try { ptr = new bla(1e10); }
    catch (...) { delete ptr; throw; }
}

class bla
{
    private:
        double *data = 0;
        // ... to not have an delete[] on a non-0 pointer
}

编辑

一个更详细的例子来说明模板类型定义答案:

#include <iostream>

using namespace std;

class bla2
{
private:
    double *data;

public:
    bla2 ()
    {
        cout << "inside bla2 constructor, enter to continue";
        cin.get();
        data = new double [2*256*256*256];
    }
    ~bla2 ()
    {
        cout << "inside bla2 destructor, enter to continue";
        cin.get();
        delete[] data;
    }
};

class bla
{
private:
    bla2 data1;
    double data2[2*256*256*256];
    double *data3;

public:
    bla ()
    {
        cout << "inside bla constructor, enter to continue";
        cin.get();
        data3 = new double [8*256*256*256]; // when only 1/4 as much -> then all success
    }
    ~bla ()
    {
        cout << "inside bla destructor, enter to continue";
        cin.get();
        delete[] data3;
    }
};

void main()
{
    bla *a;
    cout << "program start, enter to continue";
    cin.get();
    try { a = new bla; }
    catch (...) { cout << "inside catch, enter to continue"; cin.get(); exit(EXIT_FAILURE); }
    cout << "success on a, enter to continue";
    cin.get();
    delete a;
}

这个例子我可以很好地在我的机器(Win7 4GB RAM)上看到关于资源监视器,它是如何首先进入bla2() 然后bla() 但是由于分配失败data3 第一次调用~bla2()然后在catch(...) 中结束(没有~bla()),内存处于基线状态,就像程序启动时一样。

当我将 data3 元素的数量设置为只有 1/4 时,所有成功,构造函数和析构函数的调用顺序符合预期。

【问题讨论】:

    标签: c++ exception memory-leaks stack-unwinding


    【解决方案1】:

    在 C++ 中,如果 new 创建的对象的构造函数抛出异常,则语言保证为该对象分配的内存将被释放。这意味着您不应该捕获异常并释放内存,因为内存已经被释放并且您将获得未定义的行为。

    如果构造函数抛出异常,则对象的析构函数将被调用。在您的情况下,这不是问题 - 如果内部分配失败,则为数组保留的内存将被清理并且异常向外传播。你不需要做任何花哨的事情。但是,如果您有多个分配,您可能希望在构造函数中添加异常处理,以捕获任何分配失败并适当地解除分配。

    但是,有一个更简单的解决方案:不要使用new[]delete[] 来分配数组。相反,请使用std::vector,它会处理所有自己的分配和释放,并且不需要您照顾。 :-)

    希望这会有所帮助!

    【讨论】:

    • 非常有帮助,谢谢!我在我的问题中添加了一个更详细的示例来说明您所说的内容。
    猜你喜欢
    • 1970-01-01
    • 2013-04-29
    • 2020-04-09
    • 1970-01-01
    • 1970-01-01
    • 2017-03-27
    • 2013-11-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多