C++ C++;:对象切片和异常

C++ C++;:对象切片和异常,c++,exception,exception-handling,object-slicing,rethrow,C++,Exception,Exception Handling,Object Slicing,Rethrow,在一次采访中,我被问到为什么按值捕获异常会是一个问题,我回答说这会导致对象切片。这就是我在互联网上找到的,例如: 但现在我正在尝试,我找不到一个按值捕捉的切片示例。切片的常见场景(不包括例外情况)如下: 在最后一行中,调用Base赋值运算符,它只复制派生对象的Base部分。因此,b1的基础部分是从d2复制的,而b1的派生部分仍然是从d2复制的。糟糕 但是,当通过值捕获异常时,这怎么可能发生呢 我尝试了以下代码(同时使用:g++和Sun CC编译器): 所以我抛出派生异常,按值捕获它的基并重试,然

在一次采访中,我被问到为什么按值捕获异常会是一个问题,我回答说这会导致对象切片。这就是我在互联网上找到的,例如:

但现在我正在尝试,我找不到一个按值捕捉的切片示例。切片的常见场景(不包括例外情况)如下:

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

但是,当通过值捕获异常时,这怎么可能发生呢

我尝试了以下代码(同时使用:g++和Sun CC编译器):

所以我抛出派生异常,按值捕获它的基并重试,然后按值捕获派生异常,一切正常,没有任何切片。 怎么样

有人能提供一个按值捕获时切片的示例吗?

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

发件人:

重新引发当前处理的异常。放弃当前catch块的执行,并将控制权传递给下一个匹配的异常处理程序(但不传递给同一try块后的另一个catch子句:其复合语句被视为已“退出”),重用现有异常对象:不生成新对象。仅当当前正在处理异常时才允许使用此表单(如果使用其他方式,则调用std::terminate)。如果在构造函数上使用,则与函数try块关联的catch子句必须通过重新引用退出


请注意,如果替换
抛出通过
抛出x
,将抛出一个
Base
实例,该实例不会被捕获,从而导致调用
std::abort()
。事实上,切片的
派生的
不能被
捕获(派生的)
捕获(这就是为什么我们通常不喜欢切片,除非它们是比萨饼片)


作为结论,我坚持使用“按值抛出,按(常量)引用捕获”。在这个特定的示例中,您很擅长通过切片值进行捕获,但通常情况下并非如此。提供一个导致故障的捕获值示例。在您喜爱的搜索引擎上快速搜索可以帮助您找到其他情况,即按值捕获会带来麻烦。

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

Slincing更像
f(Base b){}
then
f(派生)。您是否尝试过将内锁扣中的
throw
更改为
throw x
?我支持它可能有一个effectOK,因此关于抛出和捕获异常的建议:“按值抛出,按引用捕获”实际上可以替换为“按值抛出,按任何方式捕获,使用纯抛出(不带参数)重新抛出”。(如果您想修改您的异常对象,请通过引用捕获)。@AndreyRubliov如果这个答案适合您,您可以通过检查其分数下的分数来接受它。如果不适合你,你可以提出问题/改进建议。对。按值捕获的另一个问题是,异常类的复制构造函数是否可以抛出异常(在内存不足的情况下也可以这样做)。然后将调用std::terminate()。
Derived d1;
Derived d2;
Base& b1 = d1;
Base& b2 = d2;
b1 = b2;
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 }