尝试用C ++捕获 - 创建了多少个对象

时间:2018-05-12 04:46:47

标签: c++ c++11 try-catch

请考虑以下代码:

#include <iostream>
#include <string>
class Box{
    public: 
        std::string show(){
            std::cout<<"Box show executed"<<std::endl;
            return "msg";
        };

        ~Box(){
            std::cout<<"Box destructor is executed"<<std::endl;
        };
};

int main(){

    try{
        Box obj;
        std::cout<<"Coming here"<<std::endl;
        throw obj;
    }
    catch(Box msg){
        std::cout<<"I have caught the exception: \n"<<msg.show()<<std::endl;
    }

}

在GCC编译器中输出为:

Coming here
Box destructor is executed
Box show executed
I have caught the exception:
msg
Box destructor is executed
Box destructor is executed

我的困惑是:

  1. 声明打印非常令人困惑,为什么&#34; Box显示 执行&#34;打印之前&#34;我已经发现异常&#34;线?

  2. 有三个析构函数,只有这样才有可能     创建了两个对象,一个是try中的Box对象和temp     try中的对象传递给catch块?

3 个答案:

答案 0 :(得分:3)

  

声明打印非常令人困惑,为什么&#34; Box节目被执行&#34;打印之前&#34;我已经发现异常&#34;线?

未指定函数/运算符参数的评估顺序。您的代码等同于以下内容:

operator<<(operator<<((std::cout << "I have caught the exception: \n"), msg.show()), std::endl);

首先执行以下两项中的哪一项是未定义的:

std::cout << "I have caught the exception: \n"
msg.show() // i.e. std::cout << "Box show executed" << std::endl;

在您的情况下,在调用第一个msg.show()之前评估operator <<。不同的编译器可能会以不同的顺序生成带有这些调用的代码。

  

有三个析构函数,当只创建两个对象时,这是怎么回事,一个是try中的Box对象,另一个是try块中的temp对象,它被传递给catch块?

按值抛出本地对象时:

  1. 将其复制到特殊的保留内存中以用于例外。
  2. 原始对象被销毁(1)。
  3. 应该找到异常处理程序,并将对象从特殊的保留内存复制到异常处理程序堆栈。
  4. 从特殊内存中删除复制的对象(2)。
  5. 当Exception处理程序退出时,将从其堆栈中删除一个对象(3)。

答案 1 :(得分:0)

  

为什么&#34; Box节目被执行&#34;打印之前&#34;我已经抓住了   例外&#34;线?

因为它想评估show()部分中的catch方法,所以它会首先打印字符串。

  

有三个析构函数叫

因为您正常捕捉Box,所以如果使用Box&,您将只看到2次析构函数调用。在您的代码中,对象被复制两次并且使用1个主对象,您需要3个析构函数调用。如果您按如下所示更改代码,则会看到这些复制构造函数:

#include <iostream>
#include <string>
class Box{
    public: 
        Box(){}
        Box(const Box &obj){
            std::cout <<"copy"<<std::endl;
        }

        std::string show(){
            std::cout<<"Box show executed"<<std::endl;
            return "msg";
        };

        ~Box(){
            std::cout<<"Box destructor is executed"<<std::endl;
        };
};

int main(){

    try{
        Box obj;
        std::cout<<"Coming here"<<std::endl;
        throw obj;
    }
    catch(Box msg){
        std::cout<<"I have caught the exception: \n"<<msg.show()<<std::endl;
    }    
}

结果将是:

Coming here
copy                          // <-- for throw
Box destructor is executed
copy                          // <-- when you catch at catch(Box msg)
Box show executed
I have caught the exception: 
msg
Box destructor is executed
Box destructor is executed

这些copy的用途是什么?一个用于throw obj;,另一个用于catch(Box msg)

答案 2 :(得分:0)

我认为如果将构造函数和复制构造函数添加到代码中,它将帮助您了解正在发生的事情。

#include <iostream>
#include <string>
class Box {
public:
    std::string show() {
        std::cout << "Box show executed - " << this << std::endl;
        return "msg";
    };

    Box() {
        std::cout << "Box constructor is executed " << this << std::endl;
    }

    Box(const Box& rhs) {
        std::cout << "Box copy constructor is executed " << this << std::endl;
    }

    ~Box() {
        std::cout << "Box destructor is executed " << this << std::endl;
    };
};

int main() {

    try {
        Box obj;
        std::cout << "Coming here" << std::endl;
        throw obj;
    }
    catch (Box msg) {
        std::cout << "I have caught the exception: \n" << msg.show() << std::endl;
    }

}

这会产生以下输出:

/*
Box constructor is executed 0051FB0B 
Coming here 
Box copy constructor is executed 0051FA33
Box copy constructor is executed 0051FAFF 
Box destructor is executed 0051FB0B 
Box show executed - 0051FAFF 
I have caught the exception: msg 
Box destructor is executed 0051FAFF 
Box destructor is executed 0051FA33
*/

所以你可以看到#1的答案是在throw和catch期间创建了两个新对象。紧接着,原始对象被破坏,因为它现在超出了它的块范围。

下一部分有点令人困惑,因为有两个嵌套的std :: cout语句。在这种情况下,编译器选择完全解析表达式msg.show(),然后才能生成“我遇到异常......”这一行的第一部分的输出。因此,当它解析msg.show()的值时,msg.show()首先生成了自己的输出行“Box show .....”,这就是它首先出现的原因。作为S.M.在他的回答中指出,编译器选择评估嵌套std :: cout的顺序是实现定义的,并且不保证这种操作顺序。

最后,在catch中创建的对象被破坏,然后从try中创建的对象被破坏。