在调用析构函数后使用对象

using an object after it's destructor is called

本文关键字:对象 调用 析构函数      更新时间:2023-10-16

可能的重复:
可以在其范围之外访问本地变量的内存吗?

代码:

#include <iostream>
using namespace std;
class B{
    public:
    int b;
    B():b(1){}
    ~B(){cout << "Destructor ~B() " << endl;}
};
class A{
    public:
    B ob;
    A()try{throw 4;}
    catch(...){cout << "Catched in A() handler : ob.b= " << ob.b<<endl;}
};


int main()try{
A t;

}
catch(...){cout << "CATCHED in Main" <<  endl;}

输出:

Destructor ~B() 
Catched in A() handler : ob.b= 1
CATCHED in Main

我的问题是如何访问其destructor调用对象b的成员变量CC_1。

使用破坏的对象是未定义的行为。这意味着您现在可能会得到这种行为,但是没有任何保证您会再获得其他时间。未定义的行为比常规错误更危险,因为它可能很难检测到,如本示例所示。

更新:按照一些评论,我将解释为什么OP的代码会产生该输出。

try功能块在C 中有些晦涩难懂。您可以围绕try块内部的函数的整个身体,并带有相应的catch。这是,而不是:

void foo()
{
  try
  {
    //...
  }
  catch (/*whatever*/)
  {
    //...
  }
}

您可以写:

void foo()
try
{
    //...
}
catch (/*whatever*/)
{
  //...
}

这确实不太有用,但是对于构造函数而言可能会略有用,因为这是在try块中包含初始化列表的唯一方法。因此,A构造函数的OP代码等效于:

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

我说这只是有用的,因为您无法使用 catch块内部构造的对象;如果将 try块内扔进,则例外将离开构造函数,因此该对象将永远不会构造,并且在输入catch块之前,任何构造的数据成员都会立即破坏。但它可能用于记录目的。

现在,众所周知,构造函数(Asuming我们不使用nothrow版本)只能做两件事:

  1. 返回构造的对象
  2. 投掷例外

在这个构造函数中,我们投掷了,但是例外是在catch块中捕获的。那么现在会发生什么呢?什么将返回到调用构造函数的代码?我们无法返回构造的对象,因为我们没有,所以只有一个替代方法:catch块必须投掷。在这种情况下,这实际上是标准要求的。如果我们不明确投掷,则编译器将在catch块的末端默默添加throw;指令。因此,详细说明构造函数等同于以下内容:

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

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

,因为当对象被破坏时,其占用的实际记忆仍然存在。但是,它是不确定的行为,并且可能起作用也可能不起作用。

可能是编译器中的错误。

当我运行您的代码时,将按照适当的顺序调用破坏者。输出为:

Catched in A() handler : ob.b= 1
Destructor ~B()

,我无法想象执行catch中CC_18的任何原因。例外已经在ob0中捕获。

update

我对编辑感到困惑。最初,是初始化列表尝试/捕获语法,这会有所作为。现在,我可以重现OP的输出,并且确实是UB。我崩溃了。