【问题标题】:How to catch derived (polymorphic) exception object and re-throw it to second level?如何捕获派生(多态)异常对象并将其重新抛出到第二级?
【发布时间】:2014-04-29 09:17:53
【问题描述】:

如何捕获派生(多态)异常对象并将其重新抛出到第二级? 在我的情况下,派生对象仅保留到“级别 1”。

我了解由 c++ 编译器完成的“返回值优化”。 在我看来,在下面的代码引用中,'e' 的引用在 'Level 2' 应该不再有效,但是编译器正在隐式创建本地基类 Exception 对象,并且我失去了从 'Level 0' 抛出的原始 ExtException。

我的问题是 我怎样才能将 'ExtException' 对象保留到 'Level 2' ?

#include <string>
#include <iostream>

using std::endl;

typedef std::string pstring;


class Exception
{
    public:
        Exception ( ) {
            my_id = ++static_count;
            std:: cout << "+Exception obj created.  ID: " << my_id << "  (Total: " << static_count << ")" << endl;
        }

        Exception ( const Exception &e ) {
            my_id = ++static_count;
            std:: cout << "+Exception obj created.  ID: " << my_id << "  (Total: " << static_count << ")" << endl;
        }

        virtual ~Exception() {
            --static_count;
            std:: cout << "-Exception obj deleted.  ID: " << my_id << "  (Total: " << static_count << ")" << endl;
        }

    private:
        static int static_count;
        int        my_id;
};
int Exception:: static_count = 0;



class ExtException : public Exception {
    public:
        ExtException () : Exception()
        { 
            std:: cout << "++ExtException obj created" << endl;
        }

        ExtException ( const ExtException &e ) : Exception()
        { 
            std:: cout << "++ExtException obj created" << endl;
        }

        ~ExtException () {
            std:: cout << "--ExtException obj deleted" << endl;
        }
};





void foo2 () {
    throw ExtException();   // Level 0 throw
}


void foo1 () 
{
    try {
        foo2 ();
    } catch ( Exception &e ) {
        // In my normal understanding of c++ 'e' should no longer be valid here 
        // as it was created on a stack which is no longer exists (i.e Level 0 is scoped out).
        //
        // _BUT_  (actually it is valid. and it is valid because of RVO compiler "Return value optimization"

        std:: cout << "\n--- foo1 catch" << endl;
        throw e;  // Level 1 throw
    }
}


void foo () 
{
    try {
        foo1 ();
    } catch ( Exception &e ) {
        // !!! CAUTION !!!
        // In my opinion 'e' must not be valid here as the original "ExtException" must be destroyed till 
        // this point (which is actually destroyed check output trace).
        // It is destroyed because RVO works for one level only...
        //
        // wait a minute... here the compiler has not left 'e' for FMR hit, instead it has created a new temporary object of 'Exception' class and
        //  'e' is referring to that.
        //
        // Here comes my real question!!!  'e' is no longer referring to an actual  ExtException object but the temporary one, 
        // how could I modify this code to access ExtException object created at 'Level 0'?

        std:: cout << "\n--- foo catch" << endl;
        throw e;  // Level 2 throw
    }
}


int main (void) {

    try {
        foo  ();
    } catch (...) { }
    return 1;
}

以下是上述代码的结果:

+Exception obj created.  ID: 1  (Total: 1)
++ExtException obj created

--- foo1 catch
+Exception obj created.  ID: 2  (Total: 2)
--ExtException obj deleted
-Exception obj deleted.  ID: 1  (Total: 1)

--- foo catch
+Exception obj created.  ID: 2  (Total: 2)
-Exception obj deleted.  ID: 2  (Total: 1)
-Exception obj deleted.  ID: 2  (Total: 0)

【问题讨论】:

    标签: c++ exception exception-handling try-catch


    【解决方案1】:

    [C++11: 15.1/3]: 一个 throw-expression 初始化一个临时对象,称为异常对象,其类型是通过从throw 操作数的静态类型,并将类型从“T 的数组”或“返回 T 的函数”分别调整为“指向 T 的指针”或“指向返回 T 的函数的指针”。 临时值是左值,用于初始化匹配处理程序中命名的变量(15.3)。如果异常对象的类型是不完整类型或指向不完整类型的指针(可能是 cv 限定的)void,则程序格式错误。除了这些限制和 15.3 中提到的类型匹配限制之外,throw 的操作数被完全视为调用中的函数参数(5.2.2)或返回语句的操作数。

    简而言之,您正在对它进行切片,尽管异常处理程序已经接受了引用。

    应该通过简单地编写 throw 来完成重新投掷。这正是它的目的:

    [C++11: 15.1/8]: 没有操作数的 throw-expression 会重新抛出当前处理的异常 (15.3)。 用现有的临时重新激活异常;没有创建新的临时异常对象。 [..]

    所以:

    throw e;  // no
    throw;    // yes
    

    【讨论】:

      【解决方案2】:

      对于多态异常,首选“throw”而不是“throw e”。

      参考:

      http://ptgmedia.pearsoncmg.com/images/0321113586/items/sutter_item73.pdf

      【讨论】:

      • 也许比谷歌搜索的超链接更多地解释你的答案并发布更多内容。不过你是对的。
      猜你喜欢
      • 1970-01-01
      • 2010-10-03
      • 2015-07-18
      • 2013-10-04
      • 2021-04-01
      • 1970-01-01
      • 2011-06-27
      • 2020-02-13
      • 2013-01-15
      相关资源
      最近更新 更多