在C ++中重新抛出了什么对象?

时间:2010-01-08 02:11:08

标签: c++

我对在C ++中重新抛出的对象的类型感到很困惑。例如,在上面的代码中,为什么输出241?

我的理解是,在第1行中,抛出了类Bar的对象。它在第2行中被捕获.Bar类型的对象被切成Foo类型。

但是,当重新抛出异常时,它的类型是什么?为什么要执行第3行?它不再是Foo了吗?

重新抛出的基本政策是什么?类型保持不变?或者有什么变化?

#include <iostream>
using namespace std;

class Foo{
public:
    Foo(int i): i(i) {}
    Foo(){}
    int i;
};

class Bar: public Foo{
public:
    Bar(int i): Foo(i) {}
    Bar(const Bar& b){i=b.i;}

};

int main () {
    Bar b(1);
    try{
        try{ 
          throw b;                 //Line 1
        }
        catch(Foo& e){
            e.i=2;                     //Line 2  
            cout<<e.i;
            throw;
        }
        catch(Bar& e){
            e.i = 3;
            cout<<e.i;
            throw e;
        }
    }
    catch (Bar e) {
        e.i*=2;                   //Line 3
        cout<<e.i;
    }
    catch (Foo e) {
        e.i*=3;
        cout<<e.i;
    }

    cout<<b.i;
    return 0;
}

5 个答案:

答案 0 :(得分:2)

使用throw;,当前正在处理的异常会向前传播,不会进行复制或切片。 (这里括号中的内容无关紧要:catch (...) {throw;}将始终重新抛出当前异常,这不一定与您的e相同。)

如果您将其更改为throw e;,则e可能会被复制并切片为Foo

答案 1 :(得分:2)

throw;自己抛出同一个对象。该对象实际上是一个Bar,即使你对它的引用是Foo&amp ;.所以当你说,“它被捕获在第2行.Bar类型的对象被切成Foo类型”,这是不对的。它不是通过捕获或重新抛出来切割的。

如果您将行throw;更改为throw e;,那么它将被切片,您将看到261.此外,当您按值捕获时,对象将被复制,因此可能是切片。

最后得到1的原因是对象b永远不会被抛出,并且在任何catch块中都没有被修改。 throw <expression>;会抛出其操作数的副本

答案 2 :(得分:1)

在C ++中,当你写:

throw;

在一个catch块中,"will re throw what ever was caught"。在所有情况下,这都是相同的实例(条形图b)。

答案 3 :(得分:1)

首先,你要说的是在第2行,抛出的对象的类型被切成Foo。这是不正确的。第2行没有任何内容。您正在通过引用捕获对象,这意味着类型Foo &的引用附加到Bar类型的对象。

如果捕获的对象确实是多态的,则可以多态地使用捕获的对象,捕获的对象e将表现为动态类型Bar的对象。没有切片发生在那里。

其次,切片是否发生在第2行并不重要。无论throw子句中使用的参数类型如何,不带参数的catch总是重新抛出“完整”异常对象。

如果您尝试通过throw e;重新抛出异常,则确实会发生切片,异常将作为Foo类型的对象重新抛出。

答案 4 :(得分:1)

Bar b(1);
try
{
    try
    {
      // The object b is copied into the exception area.
      // The standard does not define what or where this is
      // only that it exist and the exception is copied there.
      throw b;

      // It is more common to do the following:
      throw B(1);
      // Though technically the object is constructed then copied
      // into the exception area. The compiler can easily remove
      // the copy and create the B(1) object directly in the
      // exception area.
    } 

    // Note normally you put the most derived types first
    // So I would expect the Bar to be caught before the Foo
    // as doing it this way hides the Bar catch clause
    catch(Foo& e)
    {
        // Catches the object by reference.
        // e is a reference to the exception held in the exception area

        // This throws the exception held in the exception area.
        throw ;
    }

    // This will never be called.
    // Bar is derived from Foo. And all Foo type objects are caught by
    // the previous catch clause.
    catch(Bar& e)
    {
        // Catches the object by reference.
        // e is a reference to the exception in the exception area.

        // This throws the local variable e as the exception.
        // The variable e is copied into the exception area.
        //
        // Note that if the object was of a type derived from Bar
        // then it will be sliced as it is copied into the exception area.
        throw e;
    }   
}       
catch (Bar e)
{       
    // Catches by value.
    // Any object of type Bar (or derived from Type Bar) is caught here.
    // If the object is of a type derived from Bar then it will be sliced.
}
catch (Foo e)
{
    // Catches by value.
    // Any object of type Foo (or derived from Type Foo) EXCEPT if it was derived from Bar
    // If the object is of a type derived from Foo then it will be sliced.
}