C++ 是否应将异常链接到C++;?

C++ 是否应将异常链接到C++;?,c++,exception,chaining,nested-exceptions,C++,Exception,Chaining,Nested Exceptions,我刚刚完成了一个C++程序的工作,在这个程序中我实现了我自己的异常(尽管是从std::exception派生的)。当一个异常引起连锁反应、向上传播错误并引发其他异常时,我采用的做法是在模块(读取类)的每个适当步骤连接错误消息。即,删除旧异常本身并创建新异常,但错误消息较长 这可能对我的小项目有效,但我最终对我的方法不是很满意。首先,除了最后一个例外,行号(尽管目前未应用)和文件名不会保留;事实上,这些信息在第一个例外中最为重要 我认为将异常链接在一起可以更好地处理这一问题;i、 e.旧异常在新异

我刚刚完成了一个C++程序的工作,在这个程序中我实现了我自己的异常(尽管是从std::exception派生的)。当一个异常引起连锁反应、向上传播错误并引发其他异常时,我采用的做法是在模块(读取类)的每个适当步骤连接错误消息。即,删除旧异常本身并创建新异常,但错误消息较长

这可能对我的小项目有效,但我最终对我的方法不是很满意。首先,除了最后一个例外,行号(尽管目前未应用)和文件名不会保留;事实上,这些信息在第一个例外中最为重要

我认为将异常链接在一起可以更好地处理这一问题;i、 e.旧异常在新异常的构造函数中提供。但这将如何实施?当异常超出方法的作用域时,是否会死亡,从而阻止使用异常指针?如果异常可以是任何派生类,那么如何复制和存储该异常

<>这最终导致我考虑C++中的链接异常是否是一个好主意。也许我们应该创建一个异常,然后向它添加额外的数据(就像我一直在做的那样,但可能是以更好的方式)


你对此有何反应?由另一个异常引起的异常是否应该链接在一起以保留某种“异常跟踪”——应该如何实现或者应该使用一个异常并附加额外的数据——应该如何做?

另一个想法是将相关数据添加到异常对象中,然后使用一个简单的
抛出语句重新抛出它。我认为在这种情况下,堆栈信息会被保留,因此您仍然知道异常的原始来源,但是测试将是一个好主意


我敢打赌,因为任何堆栈信息是否可用都是由实现定义的,所以在抛出一个裸
之后,实现是否以任何方式保留堆栈信息的差异将更大语句。

如果希望数据比接收它的
catch
块的寿命长,除了通过
throw重试之外,还需要将数据从异常对象复制到链中。(例如,如果该
catch
块通过
throw obj;
退出,则其包括)

例如,可以通过将要保存的数据放在堆上,并对异常中的私有数据实现
交换
在C++0x中移动

当然,在异常情况下使用堆时需要小心……但是,在大多数现代操作系统中,内存过度使用完全阻止了
new
抛出,不管是好是坏。良好的内存余量和在完全崩溃时从链中删除异常应该可以保证安全

struct exception_data { // abstract base class; may contain anything
    virtual ~exception_data() {}
};

struct chained_exception : std::exception {
    chained_exception( std::string const &s, exception_data *d = NULL )
        : data(d), descr(s) {
        try {
            link = new chained_exception;
            throw;
        } catch ( chained_exception &prev ) {
            swap( *link, prev );
        } // catch std::bad_alloc somehow...
    }

    friend void swap( chained_exception &lhs, chained_exception &rhs ) {
        std::swap( lhs.link, rhs.link );
        std::swap( lhs.data, rhs.data );
        swap( lhs.descr, rhs.descr );
    }

    virtual char const *what() const throw() { return descr.c_str(); }

    virtual ~chained_exception() throw() {
        if ( link && link->link ) delete link; // do not delete terminator
        delete data;
    }

    chained_exception *link; // always on heap
    exception_data *data; // always on heap
    std::string descr; // keeps data on heap

private:
    chained_exception() : link(), data() {}
    friend int main();
};

void f() {
    try {
        throw chained_exception( "humbug!" );
    } catch ( std::exception & ) {
        try {
            throw chained_exception( "bah" );
        } catch ( chained_exception &e ) {
            chained_exception *ep = &e;
            for ( chained_exception *ep = &e; ep->link; ep = ep->link ) {
                std::cerr << ep->what() << std::endl;
            }
        }
    }

    try {
        throw chained_exception( "meh!" );
    } catch ( chained_exception &e ) {
        for ( chained_exception *ep = &e; ep->link; ep = ep->link ) {
            std::cerr << ep->what() << std::endl;
        }
    }
}

int main() try {
    throw chained_exception(); // create dummy end-of-chain
} catch( chained_exception & ) {
    // body of main goes here
    f();
}

您可能需要查看以下内容:


这与MS在C#中所采用的方法有些不同,但似乎符合您的要求。

由于提出了这个问题,C++11对标准做了一些值得注意的更改。 在关于异常的讨论中,我经常忽略这一点,但是下面的方法,嵌套异常,却起到了作用:

使用及 它在StackOverflow中进行了描述,并且介绍了如何在代码中获得异常的回溯,而无需调试器或繁琐的日志记录,只需编写一个适当的异常处理程序,它将重新显示嵌套的异常

由于可以对任何派生的异常类执行此操作,因此可以向此类回溯添加大量信息! 您还可以查看my,其中回溯看起来像这样:

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"

如果适用的话,这是很优雅的,但是异常的类型不能改变。正如Potatoswatter已经提到的,异常的类型不能改变,而且我发现这很麻烦,因为错误的解释在堆栈上越深入就越困难。例如,如果在访问具有错误索引的内部向量的方法中抛出IndexOutOfBoundsException,则在调用用户启动的操作的top方法中捕获IndexOutOfBoundsException没有多大意义。我在这里看到的唯一可能的方法是,要么将其链接起来,要么将其完全删除,作为替换例外。@gablin-是的,这是有道理的,我同意。不幸的是,如果您抛出一个新异常,我认为没有任何好的方法来保存有关原始异常的堆栈信息。所以你可以选择。我认为
::boost::exception
是在异常中记录信息的好方法,即使您抛出了一个新的异常。它将允许你记录原始内容,我认为这通常是一个好主意。@kbrimington:的确,这个问题触及了这个问题的核心;i、 e.例外链接(或“内部例外”);我的问题只是对此进行了进一步的探讨,并询问是否应该从一开始就做出这样的承诺,还是坚持“你应该只抛出一种方法”。忘了感谢你的链接,但评论无法再编辑^^问题的答案是使用C++ 11代码> STD::NeStdEdExpAs/Cuth>类,显然C++标准库的作者相信,链异常是一件好事。我听说过Boost,但从来没有使用过它。我会检查你张贴的链接,看看这是否符合我正在寻找的答案。谢谢。@gablin-它基本上是一种结构良好的方法,允许将数据添加到
catch
块中的异常中。这意味着您抛出的所有异常都必须派生自
::boost::exception
,但如果是,则向异常添加信息并使用
抛出重新抛出当它向上传播时
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"