这个问题在这里已经有一个答案:

我刚刚完成了C ++的工作 - 我实现了自己的异常(尽管源自STD ::异常)。当一个例外引起链反应,向上传播错误并引起其他例外时,我应用的练习是在整个模块上的每个适当步骤(读取类)中加成错误消息。即掉落旧的异常本身,并创建了一个新的异常,但带有较长的错误消息。

这可能对我的小计划有用,但最终我对方法并不感到非常满意。首先,除了最后一个例外,线号(尽管目前尚未应用)和文件名没有保留;实际上,在第一个例外,该信息最感兴趣。

我认为可以通过将异常链式链式来处理更好。即新异常的构造函数中提供了旧例外。但是如何实施呢?当它们从该方法中脱离范围时,没有例外,从而阻止了他们使用异常指针?以及如何复制和存储异常,如果异常是任何派生类的类别?

最终,这使我考虑C ++中的链式例外是否毕竟是一个好主意。也许应该只创建一个例外,然后添加其他数据(就像我一直在做的那样,但可能以更好的方式)?

您对此有何反应?是否应该将另一个链式链接在一起以保留某种“异常跟踪”而引起的例外 - 应该如何实施? - 或者应该使用单个例外并附加了其他数据 - 应该如何完成?

有帮助吗?

解决方案

如果您希望它超过链,则有必要将数据从异常对象,链中复制到链 catch 收到它的块,除了重新 throw;. 。 (例如,如果那样 catch 块通过一个 throw obj;.)

这可以通过将数据保存在堆上并实施来完成 swap (move 例如,在c ++ 0x中)在您的私人数据中,例如。

当然,在使用堆时,您需要小心,但在大多数现代OS中,记忆过度的承诺完全阻止 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();
}

输出(适当脾气):

bah
humbug!
meh!

其他提示

由于这个问题已被询问,已使用C ++ 11进行了明显的更改。在讨论异常的讨论中,我一直缺少这一点,但是以下方法(嵌套例外)可以解决问题:

采用 std::nested_exceptionstd::throw_with_nested

它在stackoverflow上进行了描述 这里这里, , 你怎么能 在您的例外情况下获得回溯 在您的代码中,无需进行调试器或繁琐的记录,只需编写适当的异常处理程序,该处理程序会重新嵌套异常。

由于您可以在任何派生的异常类中执行此操作,因此您可以在这样的回溯中添加很多信息!你也可以看看我的 MWE在Github上, ,回溯看起来像这样:

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"

您可能想看看: http://www.boost.org/doc/libs/1_43_0/libs/exception/doc/boost-exception.html

这与MS在C#中所做的事情有些不同,但似乎符合您的要求。

另一个想法是将相关数据添加到您的异常对象,然后使用Bare throw; 声明重新启动。我认为在这种情况下保留了堆栈信息,因此您仍然知道例外的原始来源,但是测试是一个好主意。

我敢打赌,由于是否有任何堆栈信息是定义的,是否有任何堆栈信息,该实现在裸露后是否以任何方式保存在任何方式上会更加广泛 throw; 陈述。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top