请考虑以下代码:
#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
我的困惑是:
声明打印非常令人困惑,为什么&#34; Box显示 执行&#34;打印之前&#34;我已经发现异常&#34;线?
有三个析构函数,只有这样才有可能 创建了两个对象,一个是try中的Box对象和temp try中的对象传递给catch块?
答案 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 :(得分: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中创建的对象被破坏。