C ++:对象切片和异常

时间:2018-05-18 11:37:17

标签: c++ exception exception-handling object-slicing rethrow

在一次采访中,我被问到为什么按价值捕获异常可能是一个问题,我回答说这会导致对象切片。这就是我在互联网上找到的内容,例如:https://www.viva64.com/en/w/v746/

但是现在我正在尝试进行实验,但是在按值捕获时我找不到切片的示例。切片的常用场景(不是例外)是这样的:

Derived d1;
Derived d2;
Base& b1 = d1;
Base& b2 = d2;
b1 = b2;

在调用Base的最后一行赋值运算符中,它仅复制Derived对象的Base部分。因此,b1的基础部分是从d2复制的,而b1的派生部分是从d2复制的。 BAD。

但是,如果按值捕获异常,怎么会发生这种情况?

我尝试了这段代码(同时包含:g ++和Sun CC编译器):

struct Base
{
    virtual void print() const
    {
        cout << "{ Base: " << m << " }" << endl;
    }

    Base(int _m = 0) : m(_m) {}

    int m;
};

struct Derived : Base
{
    Derived(int _m = 0, int _n = 0) : Base(_m), n(_n) {}

    void print() const
    {
        cout << "{ Base: " << m << ", Derived: " << n << " }" << endl;
    }

    int n;
};

int main()
{
    try
    {
        try
        {
            throw Derived(3, 300);
        }
        catch(Base x)
        {
            cout << "Inner catch: ";
            x.print();
            throw;
        }
    }
    catch(Derived y)
    {
        cout << "Outer catch: ";
        y.print();
    }    
}

输出结果为:

Inner catch: { Base: 3 }
Outer catch: { Base: 3, Derived: 300 }

所以我抛出Derived异常,捕获它的Base BY VALUE并重新抛出,然后捕获Derived BY VALUE并且一切正常,没有任何切片。 那怎么样?

有人可以在捕获BY VALUE时提供切片示例吗?

2 个答案:

答案 0 :(得分:5)

即使catch(Base)对抛出的Derived对象进行切片,re throw也会使用原始异常对象而不是切片副本。

来自http://en.cppreference.com/w/cpp/language/throw

  

重新显示当前处理的异常。放弃当前catch块的执行并将控制传递给下一个匹配的异常处理程序(但不是在同一个try块之后的另一个catch子句:它的复合语句被认为已经退出&#39;),< strong>重用现有的异常对象:不会创建新对象。此表单仅在当前正在处理异常时允许(如果另外使用则调用std :: terminate)。如果在构造函数上使用,则与函数try-block关联的catch子句必须通过rethrowing退出。

请注意,如果您将throw;替换为throw x;,则会抛出Base个实例并且不会被捕获,从而导致std::abort()被调用。实际上,切片Derived不能被取消(这就是为什么我们通常不喜欢切片,除非它们是披萨切片)被catch (Derived)捕获。< / p>

作为结论,我坚持使用&#34;按值抛出,按(const)引用捕获)&#34; 。在这个具体的例子中,你很好地捕捉切片值,但一般情况并非如此。 Serge Ballesta's answer提供了一个导致问题的价值追赶的例子。快速搜索您最喜欢的搜索引擎可以帮助您找到其他可以追逐价值的案例。

答案 1 :(得分:1)

通过值捕获异常的另一个问题是它需要异常的完整副本。如果您接近StackOverflow条件(或已经处理一个条件),则可能处于无法执行复制的用例,并且无法执行catch子句。