C++ 在其后面使用对象';s的析构函数被称为

C++ 在其后面使用对象';s的析构函数被称为,c++,exception,destructor,C++,Exception,Destructor,可能重复: 代码: 我的问题是如何能够访问其析构函数调用已完成的对象的成员变量b。使用析构函数对象是未定义的行为。这意味着你现在可能会有这种行为,但没有什么能保证你会在其他时间得到它。如本例所示,未定义的行为比常规错误更危险,因为它可能更难检测 更新:在一些注释之后,我将解释为什么OP的代码会产生那个输出 代码>尝试< /COD>函数块是C++中有点模糊的特性。您可以在try块中用相应的catch包围函数的整个主体。这是,而不是: void foo() { try { //..

可能重复:

代码:


我的问题是如何能够访问其析构函数调用已完成的对象的成员变量
b

使用析构函数对象是未定义的行为。这意味着你现在可能会有这种行为,但没有什么能保证你会在其他时间得到它。如本例所示,未定义的行为比常规错误更危险,因为它可能更难检测

更新:在一些注释之后,我将解释为什么OP的代码会产生那个输出

<>代码>尝试< /COD>函数块是C++中有点模糊的特性。您可以在
try
块中用相应的
catch
包围函数的整个主体。这是,而不是:

void foo()
{
  try
  {
    //...
  }
  catch (/*whatever*/)
  {
    //...
  }
}
你可以写:

void foo()
try
{
    //...
}
catch (/*whatever*/)
{
  //...
}
实际上,这并不太有用,但对于构造函数来说可能用处不大,因为这是在
try
块中包含初始化列表的唯一方法。因此,OP的
A
构造函数代码相当于:

A()
try
: b()
{
  throw 4;
}
catch(...)
{
  cout << "Catched in A() handler : ob.b= " << ob.b<<endl;
}

这就是异常被捕获两次的原因:一次在
A
构造函数中,一次在
main()

中,使用析构函数对象是未定义的行为。这意味着你现在可能会有这种行为,但没有什么能保证你会在其他时间得到它。如本例所示,未定义的行为比常规错误更危险,因为它可能更难检测

更新:在一些注释之后,我将解释为什么OP的代码会产生那个输出

<>代码>尝试< /COD>函数块是C++中有点模糊的特性。您可以在
try
块中用相应的
catch
包围函数的整个主体。这是,而不是:

void foo()
{
  try
  {
    //...
  }
  catch (/*whatever*/)
  {
    //...
  }
}
你可以写:

void foo()
try
{
    //...
}
catch (/*whatever*/)
{
  //...
}
实际上,这并不太有用,但对于构造函数来说可能用处不大,因为这是在
try
块中包含初始化列表的唯一方法。因此,OP的
A
构造函数代码相当于:

A()
try
: b()
{
  throw 4;
}
catch(...)
{
  cout << "Catched in A() handler : ob.b= " << ob.b<<endl;
}

这就是异常被捕获两次的原因:一次在
A
构造函数中,一次在
main()

中,因为当对象被销毁时,它占用的实际内存仍然存在。但是,它是未定义的行为,可能有效,也可能无效。

因为当对象被销毁时,它占用的实际内存仍然存在。但是,这是未定义的行为,可能有效,也可能无效。

这可能是编译器中的错误

当我运行代码时,析构函数按正确的顺序调用。输出为:

Catched in A() handler : ob.b= 1
Destructor ~B()
我无法想象执行
main
中的
catch
的任何原因。异常已在
A::A()
中捕获

更新


我被编辑弄糊涂了。最初,是初始值设定项列表try/catch语法起了作用。现在我可以复制OP的输出,它确实是UB。我崩溃了。

这可能是编译器中的错误

当我运行代码时,析构函数按正确的顺序调用。输出为:

Catched in A() handler : ob.b= 1
Destructor ~B()
我无法想象执行
main
中的
catch
的任何原因。异常已在
A::A()
中捕获

更新


我被编辑弄糊涂了。最初,是初始值设定项列表try/catch语法起了作用。现在我可以复制OP的输出,它确实是UB。我撞车了。

这不是同一个问题,但埃里克的答案仍然适用:@R.MartinhoFernandes:啊,这么多年的经典之作之一:)带来了。这不是同一个问题,但埃里克的答案仍然适用:@R.MartinhoFernandes:啊,经典名著之一:)带来了。你说它所占据的实际记忆仍然存在。这是否意味着该内存将一直存在,直到“B”类型的“ob”对象被释放为止,也就是当“A”类型的“t”对象被释放时?@Alexan:A类型的对象从未存在过,因为它从未完全构造过,并且
B
对象内存已经被释放。内存已被释放,但其内容仍然存在,因为它尚未被重用。然而,你不能依赖它;没有任何东西可以保证运行时不会决定在以后的代码运行中重用此内存(当然可能是在客户面前)。您说过它占用的实际内存仍然存在。这是否意味着该内存将一直存在,直到“B”类型的“ob”对象被释放为止,也就是当“A”类型的“t”对象被释放时?@Alexan:A类型的对象从未存在过,因为它从未完全构造过,并且
B
对象内存已经被释放。内存已被释放,但其内容仍然存在,因为它尚未被重用。然而,你不能依赖它;没有任何东西可以保证运行时不会决定在将来的代码运行中重用此内存(当然可能是在客户面前)。事实上,编译器不符合标准。只要
try
块离开,就应该调用该析构函数。无论如何,由于我们在这里遇到了未定义的行为,在
catch
块之后调用析构函数可能是一种可以接受的优化(不是很确定)。@Gorpik:异常永远不会逃过'A``的构造函数,所以我认为这是OP示例中的一个bug。@Gorpik:可能吧。我的编译器是VC:)你有解释为什么在OP的情况下执行
main
中的
catch(…)
吗?很抱歉,我没有对我的问题进行编辑或批准任何编辑,编辑我代码的人对代码进行了更改。是的,有人没有这样做