1

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

代码:

#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

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

4

3 回答 3

8

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

更新:在一些评论之后,我将解释为什么 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块之前立即被破坏。但它可能有一些用于记录目的。

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

  1. 返回一个构造对象
  2. 抛出异常

在这个构造函数中,我们抛出异常,但异常被捕获在catch块中。那么现在会发生什么?调用构造函数的代码会返回什么?我们不能返回一个构造的对象,因为我们没有,所以只有一个选择:catch块必须抛出。这实际上是标准在这种情况下的要求。如果我们不显式地抛出,编译器将throw;在块的末尾默默地添加一条指令catch。因此,详细说明一下,构造函数等效于以下内容:

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

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

于 2012-10-25T11:42:29.143 回答
3

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

于 2012-10-25T11:43:31.717 回答
2

这可能是您的编译器中的错误。

当我运行您的代码时,会以正确的顺序调用析构函数。输出是:

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

catch而且我无法想象执行in的任何原因main。异常已经在A::A().

更新

我被编辑弄糊涂了。最初,初始化列表的 try/catch 语法有所不同。现在我可以重现 OP 的输出,它确实是 UB。我撞车了。

于 2012-10-25T11:55:20.930 回答