简体   繁体   English

创建二维数组的异常:在 C++ 中重新抛出之前进行清理

[英]exception creating 2D Array: clean-up before re-throw in C++

I want to have a function which dynamically creates and returns a 2D array or when memory allocation fails passes the exception without information loss after cleaning up already allocated rows:我想要一个动态创建并返回二维数组的函数,或者当内存分配失败时,清理已分配的行传递异常而不会丢失信息:

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;
}

So I can be sure, if create throws, there is no memory leak.所以我可以肯定,如果 create 抛出,则没有内存泄漏。 But can I be sure, that the passed exception e is "the same" as if directy thrown by new and not catched?但是我可以确定,传递的异常 e 与 new 直接抛出但未捕获一样“相同”?

Eg例如

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;
}

Or is it necessary to have catch (bad_alloc &e) inside the create function?或者是否有必要在create函数中使用catch (bad_alloc &e) Or does it work only with catch (...) { /* do clean-up*/ throw; }或者它只适用于catch (...) { /* do clean-up*/ throw; } catch (...) { /* do clean-up*/ throw; } ? catch (...) { /* do clean-up*/ throw; } Is there the same problem as in C# with losing stack trace when re-throw is not with throw;当重新抛出不是throw;是否存在与 C# 中丢失堆栈跟踪相同的问题throw; simply?简单?

And another, more general question:还有一个更普遍的问题:

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?
    }
}

For exception-safe memory management, use RAII .对于异常安全的内存管理,请使用RAII Rather than juggling raw pointers and exception handlers, assign the resource to a class which will release it on destruction.与其处理原始指针和异常处理程序,不如将资源分配给一个类,该类将在销毁时释放它。 That way, everything is cleaned up automatically if an exception is thrown.这样,如果抛出异常,一切都会自动清理。

In this case, std::vector is a suitable RAII class managing a dynamic array:在这种情况下, std::vector是管理动态数组的合适 RAII 类:

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

(Note that it may be more efficient to represent the 2D array as a single array of size rows*cols , with accessors to provide 2D indexing into it. But that's off-topic for this question, so I won't go into tedious detail). (请注意,将 2D 数组表示为大小为rows*cols的单个数组可能更有效,并使用访问器为其提供 2D 索引。但这对于这个问题来说是题外话,所以我不会进入繁琐的细节)。

To answer your questions, although they're largely irrelevent if you write exception-safe code:回答您的问题,尽管如果您编写异常安全的代码,它们在很大程度上是无关紧要的:

But can I be sure, that the passed exception e is "the same" as if directy thrown by new and not catched?但是我可以确定,传递的异常 e 与 new 直接抛出但未捕获一样“相同”?

It won't be;不会的; you're throwing a new object, created by copying or moving e , with type exception .您正在抛出一个通过复制或移动e创建的新对象,类型为exception

Or is it necessary to have catch (bad_alloc &e) inside the create function?或者是否有必要在 create 函数中使用catch (bad_alloc &e)

Then you won't catch other types of exceptions.那么你就不会捕捉到其他类型的异常。 In this case, that might not a problem, but you really want to be catching all exceptions if you're going to be cleaning up like this.在这种情况下,这可能不是问题,但是如果您要像这样进行清理,您确实希望捕获所有异常。 To reiterate: don't use exception handlers to clean up.重申:不要使用异常处理程序进行清理。 It's very error-prone.它非常容易出错。

Or does it work only with catch (...) { /* do clean-up*/ throw; }或者它只适用于catch (...) { /* do clean-up*/ throw; } catch (...) { /* do clean-up*/ throw; } ? catch (...) { /* do clean-up*/ throw; }

That will rethrow the original object, which is what you want.这将重新抛出原始对象,这正是您想要的。 (Except that you shouldn't want to be catching anything in the first place). (除了您一开始不应该想要捕捉任何东西)。

Is there the same problem as in C# with losing stack trace when re-throw is not with throw;当重新抛出不是throw;是否存在与 C# 中丢失堆栈跟踪相同的问题throw; simply?简单?

Standard exceptions don't give you a stack trace anyway.无论如何,标准异常都不会给您堆栈跟踪。 If you have a custom exception type with a stack trace, then it depends on whether it generates a new one when copied/moved, or copies/moves the existing one.如果您有一个带有堆栈跟踪的自定义异常类型,那么这取决于它是在复制/移动时生成新异常类型,还是复制/移动现有异常类型。

Where is the Object or C-String?对象或 C 字符串在哪里?

The exception handling mechanism creates it somewhere (not on the stack which is about to be unwound), and destroys it once it's been handled.异常处理机制在某处(不在即将展开的堆栈上)创建它,并在处理后销毁它。 It's not specified exactly where it is, just how it must work.它没有具体说明它在哪里,只是说明它必须如何工作。

While this question is quite old by now and has been answered adequately, I would like to add a note on rethrowing exceptions.虽然这个问题现在已经很老了并且已经得到了充分的回答,但我想添加一个关于重新抛出异常的注释。 In standard C++11 you can retain the original exception information by rethrowing with:标准 C++11 中,您可以通过重新抛出来保留原始异常信息:

std::nested_exception and std::throw_with_nested std::nested_exceptionstd::throw_with_nested


FYI: using this, you can also generate exception backtraces, as described on StackOverflow here and here , without need for a debugger or cumbersome logging, by simply writing a proper exception handler which will rethrow nested exceptions.仅供参考:使用它,您还可以生成异常回溯,如 StackOverflow此处此处所述,无需调试器或繁琐的日志记录,只需编写适当的异常处理程序即可重新抛出嵌套异常。

Since you can do this with any derived exception class, you can add a lot of information to such a backtrace!由于您可以使用任何派生的异常类来执行此操作,因此您可以向此类回溯添加大量信息! You may also take a look at my MWE on GitHub , where a backtrace would look something like this:您也可以在 GitHub 上查看我的MWE ,其中的回溯如下所示:

Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"

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

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