创建2D数组的例外:在重新放入C ++之前进行清理

问题描述 投票:1回答:2

我想拥有一个动态创建并返回2D数组的函数,或者当内存分配失败时通过异常without信息丢失after清理已分配的行:

double **create (int rows, int cols)
{
    double **array = new double* [rows];
    for (int x=0; x<rows; x++)
    {
        try { array[x] = new double [cols]; }
        catch (exception &e)
        {
            x--;
            for (; x>=0; x--)
                delete[] array[x]; // clean up already allocated rows
            delete[] array;
            throw e; // pass exception
        }
        for (int y=0; y<cols; y++)
            array[x][y] = 0; // initialize array
    }
    return array;
}

因此,我可以肯定的是,如果create throws引发了内存泄漏。但是我可以确定,所传递的异常e是“相同的”,就像被new直接抛出并没有被捕获一样?

例如

int main ()
{
    double **d;
    try { d = create (HUGE_x, HUGE_y); }
    catch (exception &e)
    {
        // 1. e could be thrown by new double* [rows]
        //      e.g. if HUGE_x is already to huge
        // 2. e could be thrown by throw e
        //      e.g. if after some rows no memory anymore
        // in both cases: is e the same?
    }
    return 0;
}

或者是否需要在catch (bad_alloc &e)函数中包含create?还是仅适用于catch (...) { /* do clean-up*/ throw; }?是否存在与C#中相同的问题,当重新抛出不是简单地使用throw;时丢失堆栈跟踪吗?

还有另一个更普遍的问题:

void f () { throw Object(); } // or throw "help";

void main ()
{
    try { f(); }
    catch (Object &e) // or catch (char *)
    {
        // Where is the Object or C-String created on the stack in function f()
        // since we aren't any more in function f() but we are dealing with
        // references/pointers to a non-existent stack?
    }
}
c++ exception stack-trace multidimensional-array rethrow
2个回答
4
投票

对于异常安全的内存管理,请使用RAII。与其使用原始指针和异常处理程序,不如将资源分配给一个类,该类将在销毁时释放它。这样,如果引发异常,一切都会自动清除。

在这种情况下,std::vector是管理动态数组的合适RAII类:

vector<vector<double>> create (int rows, int cols) {
    return vector<vector<double>>(rows, vector<double>(cols));
}

(请注意,将2D数组表示为大小为rows*cols的单个数组可能会更有效,并且可以使用访问器为其中提供2D索引。但是,这个问题不在本文的讨论范围内,所以我不会进入乏味的细节)。

要回答您的问题,尽管如果您编写异常安全代码,它们在很大程度上是无关紧要的:

但是我可以确定,所传递的异常e是“相同的”,就像新的直接抛出并没有捕获到的一样?

不会;您将抛出一个通过复制或移动e创建的,类型为exception的新对象。

或者是否有必要在创建函数中包含catch (bad_alloc &e)

那么您将不会捕获其他类型的异常。在这种情况下,这可能不是问题,但是如果您要像这样进行清理,那么您真的想捕获all异常。重申一下:不要使用异常处理程序进行清理。这非常容易出错。

或者它仅适用于catch (...) { /* do clean-up*/ throw; }吗?

将重新扔掉您想要的原始对象。 (除了您不应该一开始就捕捉任何东西之外。)

是否与C#一样存在同样的问题,当重新抛出不是简单地用throw;时丢失堆栈跟踪吗?

标准异常无论如何都不会给您堆栈跟踪。如果您具有带有堆栈跟踪的自定义异常类型,则取决于它是在复制/移动时生成新的还是在复制/移动现有的异常时生成新的异常。]

对象或C字符串在哪里?

异常处理机制在某个地方(不在即将解散的堆栈上)创建它,并在对其进行处理后销毁它。它没有确切说明它在哪里,而是它必须如何工作。


0
投票

虽然这个问题到现在为止已经很老了,并且已经得到了足够的回答,但是我想补充说明重新抛出异常。在standard C ++ 11

© www.soinside.com 2019 - 2024. All rights reserved.