通过引用

时间:2015-10-17 16:27:23

标签: c++ exception-handling

我有3个问题:

1。总是说通过引用捕获异常对象。在下面的示例中,我看到在执行 catch 块之前调用了析构函数,这意味着我们引用了 catch 中的一个对象,该对象必须超出范围。我们使用它的时间。为什么要使用参考呢?

class MyException{
public:
    ~MyException()  {
        cout<<"Dtor for MyException called \n";
    }
};
int main()
{
    try{
    MyException obj1;
    throw obj1;
}
catch(MyException& obj)
{
    cout<<"Catched unhandled exception";
}

2. 为什么析构函数在这里被调用两次?在进入 catch 块之前,在执行 catch 之后第二次完成。

3. Lifetime of a thrown object caught by reference中的示例显示析构函数只被调用一次,即在 catch 块退出后,只有复制构造函数< / em>在类中定义。

a。当我们通过引用来捕捉它时,复制构造函数的作用是什么?

b。即使在定义了复制构造函数之后,它也永远不会被调用。那它会产生什么影响?

c。我试图在我的例子中定义一个拷贝构造函数,如下所示,但我仍然看到析构函数被调用两次。为什么?:

MyException(const MyException& obj)
{
}

有人可以回答所有5个问题(第3个问题有3个部分)。

3 个答案:

答案 0 :(得分:2)

您正在将您创建的例外副本作为本地临时文件抛出。所以有两个:第一个在你抛出时被销毁,第二个(副本)在catch块完成后被销毁。

试试这个:

try { throw MyException{}; }
catch (MyException const& obj) { }

编辑:如果您的代码中没有调用您的复制构造函数,那么我的猜测是编译器已经识别出异常类为空?编译器可以自由地执行它选择的任何优化,只要代码的行为是 - 如果它没有。对此的例外是copy-ellision,但如果发生这种情况,你就不会得到双析构函数。

答案 1 :(得分:2)

  1. throw总是复制你正在投掷的物体。您不能抛出不可复制的对象。事实上,如果throw无法为副本分配足够的内存,try甚至会失败。

    这可以解释为什么你看到在obj1 - 块内调用析构函数。它是您本地obj1的析构函数。一旦你扔掉它的副本,它就会超出范围。

    至于通过引用捕获,它用于避免不必要的复制,更重要的是,潜在的切片。
  2. 再次,它是您当地throw的析构函数。当你扔掉它的副本时它会超出范围。
  3. a)如果您通过引用捕获,则不执行复制。仅当您按值捕获时,才会执行复制(具有可能的切片)。

    b)你错了,{{1}}调用一个拷贝构造函数,然后复制你的对象并抛出它。不会调用复制构造函数的唯一情况是抛出临时对象而编译器省略副本。

    c)参考我的第一个和第二个答案。

答案 2 :(得分:1)

他们建议您通过引用获取catch块以避免切片(因为继承异常是标准的)并避免不必要的复制,因为throw无条件地复制。

  1. 您无法通过引用收到obj1,因为throw obj1会导致它被销毁(因为它不再在范围内)。由于此throw始终复制到您无法控制的临时内存位置。

  2. obj1以及我之前提到过的临时文件。 obj是对该临时值的引用,引用的临时值将在catch块结束后和下一个操作之前被销毁(类似于它实际上是catch块的本地)。

  3. 单个析构函数仅在复制被删除且标准不能保证何时发生时才会发生,因为复制省略总是可选的。定义复制构造函数没有区别,因为如果您没有定义它(并且可以创建它),编译器会为您定义它。删除复制构造函数只会导致throw非法。

    • 一个。如上所述,复制构造函数复制到临时。参考参考说临时。

    • 湾你真的删除了复制构造函数吗? MyException(const MyException&) = delete;是如何删除复制构造函数,未能定义它只是让编译器为您创建一个。重复一遍,删除它会导致throw非法。

    • ℃。因为副本没有被删除,并且存在两个对象,一个临时文件和你的对象。