2016-11-04 49 views
1

比如我有一个简单的代码:C++ new操作符是如何工作的内部

class B 
{ 
}; 

class A 
{ 
    B b; 
    public: 
    A() 
    { 
     throw 1; 
    } 
}; 

int main() 
{ 
    A* a = 0; 
    try 
    { 
     a = new A; 
    } 
    catch (int) 
    { 
    } 
} 

构造函数抛出异常,那么析构函数不会被调用。但是B的析构函数会被调用。堆中的内存不会被分配。我的问题如何在内部工作?首先会是:构造A还是在堆中分配内存?那么,如果分配是第一个,如果存在异常,将如何解除分配?别的,如果构建A是第一个,它是如何应对堆的?

回答

4
  1. 内存已分配。
  2. A的构造函数被调用。
  3. A的构造函数调用B的构造函数。
  4. A的构造函数抛出。
  5. 作为标准异常处理过程的一部分,B的析构函数被调用(RAII在工作中)。
  6. 堆栈展开给调用者(main)。
  7. 内存解除分配(因为对象未成功构建)。

A的析构函数未被调用,因为对象没有被完全构造。然而,分别为的成员仍然被破坏。

内存自动释放的方式与控制离开块时局部变量被销毁的方式几乎相同。如果您熟悉Java和/或C#,请将其视为看不见的try-finally构造。或者一系列这样的结构。

+0

谢谢,你的评论很好,但我需要更多的信息。在堆内存中提供析构函数的机制是什么? – M90

+0

@ M90,我认为这个机制是特定于实现的。就像我说的,一个看不见的'try-finally'块。就像,如果出现问题,控制权转移到一些自动生成的代码,负责照顾任何需求,然后继续传播异常。编译你的代码到程序集中,看看它*真正的作用,如果你真的好奇。 –

+1

@ M90另请参见草药萨特的[构造失败...](http://www.gotw.ca/publications/mill13。htm) –

0

什么将优先:构建A或在堆中分配内存?

除非有记忆,否则没有空间可以构建对象。内存分配总是优先;那么代码将继续进行初始化。这适用于各种内存分配,而不仅仅是动态变化,因为构造函数在开始初始化对象之前需要有一个有效的地址this

如果分配是第一个,如果有异常会如何解除分配?

编译器发出处理“魔术”的特殊代码。此代码将为所有基类以及成员(例如B)运行析构函数,这些成员在代码输入A的构造函数时已完全构建。

+0

谢谢,请您详细介绍一下这个“特殊代码”行为吗? – M90

+0

@ M90我已经做了:对已经完全构建的所有东西运行析构函数,然后将分配的内存返回到它来自的地方(在你的情况下,它来自堆,所以回到堆中)。 – dasblinkenlight