这种链接异常的实现如何工作?

时间:2010-08-25 14:18:03

标签: c++ exception chaining nested-exceptions

我之前曾问过question关于如何在C ++中链接异常的问题,其中一个答案为如何完成它提供了一个很好的解决方案。问题是我不理解代码,试图在评论中进行这种讨论太麻烦了。所以我认为最好完全开始一个新问题。

代码包含在下面,我已经清楚地标记了我没有得到的每个部分。代码下方包含了我不理解的描述。代码由Potatoswatter编写。


代码


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;

            // ----------------------------------------------------------------
            //   How does this work (section 1)?
            throw;
            // ----------------------------------------------------------------

        } catch ( chained_exception &prev ) {
            // ----------------------------------------------------------------
            //   How does this work (section 2)?
            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() {
        // --------------------------------------------------------------------
        //   How does this work (section 3)?
        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 ) {
                // Print ep->what() to std::cerr
            }
        }
    }

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

int main() try {
    // ------------------------------------------------------------------------
    //   How does this work (section 4)?
    throw chained_exception(); // create dummy end-of-chain
    // ------------------------------------------------------------------------
} catch( chained_exception & ) {
    // body of main goes here
    f();
}

运行代码会提供以下输出:

bah
humbug!
meh!

我不明白

  1. throw; try内部 - 阻止:我以前从未见过这个。我认为throw;唯一有效的地方是catch - 阻止重新抛出捕获的内容。那这是做什么的呢?一些调试显然表明抛出的异常是之前抛出的异常,但那是在一个完全不同的try - 块内。事实上,它甚至超出了struct声明!

  2. 交换字段:为什么我们需要交换例外字段?难道只是复制指针就足够了吗?这是为了防止字段指向的结构过早地从堆中删除吗?

  3. 检查link link的链接:我可以理解link不是{{ 1}}(即使删除NULL指针无效),但为什么需要检查NULL的链接?

  4. 抛出虚拟异常:为什么需要这个假人?它被抛出然后掉了下来。为什么我们需要这作为链的终点?

1 个答案:

答案 0 :(得分:4)

聪明的代码 - 在这个问题上对potatoswatter的称赞。我想我必须找到最后一项的方法。

  1. throw;重新抛出活动异常。仅当堆栈中有catch块时才有效。我不记得我在哪里看到那个小窍子,但在某些其他问题的背景下可能是在SO上。裸抛给我们通过在chained_exception构造函数中捕获它来访问当前异常。换句话说,构造函数中的prev是对我们当前正在处理的异常的引用。

  2. 你在这里是对的。这可以防止双重删除。

  3. 永远不应删除main中引发的哨例异常。此例外的一个标识属性是link成员NULL

  4. 这是我不喜欢的部分,但想不到一个简单的方法。只有chained_exception块处于活动状态时,才能调用唯一可见的catch构造函数。 IIRC,没有活跃catch区块的裸投是禁止的。因此,解决方法是投入main并将所有代码放在catch块中。

  5. 现在,如果您在多线程代码中尝试此方法,请确保您很好地理解(4)。您必须在线程入口点复制它。

相关问题