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

try and catch in C++ - how many objects are created

本文关键字:创建 多少 对象 C++      更新时间:2023-10-16

请考虑以下代码:

#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. 声明印刷品很混乱,为什么"盒子秀"是 在"我已捕获异常"行之前执行"打印?

  2. 有三个析构函数称为,当只有 创建两个对象,一个是 try 中的 Box 对象,另一个是 temp 对象 尝试中的对象被传递给 catch 块?

语句打印非常混乱,为什么"Box show 已执行"打印在"我已捕获异常"行之前?

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

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 对象,另一个是传递给 catch 块的 try 中的临时对象?

按值抛出本地对象时:

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

为什么在"我抓住了 例外"行?

因为它想在catch部分中计算show()方法,所以它将首先打印字符串。

有三个析构函数称为

因为您正常捕获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)的。

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

#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的答案是在投掷和接球期间创建了两个新对象。紧接着,原始对象将被销毁,因为它现在超出了其块范围。

下一部分有点令人困惑,因为有两个嵌套的 std::cout 语句。在这种情况下,编译器选择在生成行的第一部分"我已捕获异常..."的输出之前完全解析表达式 msg.show((。因此,在解析 msg.show(( 的值时,msg.show(( 首先生成了自己的输出行"Box show ....."这就是为什么它排在第一位。正如 S.M. 在他的回答中指出的那样,编译器选择计算嵌套 std::cout 的顺序是实现定义的,并且不能保证这种操作顺序。

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