2010-01-08 32 views
1

我对使用C++重新生成的对象的类型非常困惑。例如,在上面的代码中,为什么输出241?在C++中重新抛出了什么对象?

我的理解是,在第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; 
} 

回答

2

throw;自己抛出相同的对象。该对象实际上是一个酒吧,即使您对它的引用是Foo &。所以当你说,“它在第2行被捕获了。Bar类型的对象被切成Foo类型”,这是不对的。它不会被捕捉或重新抛掷。

如果将行throw;更改为throw e;,则会将其切片,然后您将看到261.此外,当您按值捕获时,该对象将被复制,因此可能会被切片。

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

+0

事实上,它在这里被引用捕获并不重要。 – ephemient 2010-01-08 02:22:00

+0

是的,重新抛出什么并不重要。这对于catch块中可见的内容以及打印的内容很重要,因为提问者似乎认为逐引用会切片。 – 2010-01-08 02:26:46

2

使用throw;当前正在处理的异常是向前传播的,没有复制或切片。 (不要紧,什么是在这里的括号:catch (...) {throw;}总是会重新抛出当前的异常,这不一定是一样的e。)

如果你把它改成throw e;e可以被复制并切成Foo

1

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

如果捕获的对象是多态的,并且捕获的对象e将表现为动态类型为Bar的对象,则可以多态使用捕获的对象。没有任何切片发生。其次,切片是否出现在第2行并不重要,其实并不重要。 A throw不带参数总是会重新抛出“完整”异常对象,无论catch子句中使用的参数类型如何。

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

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. 
} 
相关问题