物体究竟在什么时候"created"?

At what moment is an object exactly "created"?

本文关键字:created 什么时候 究竟      更新时间:2023-10-16

假设我们有一个带有构造函数的类:

class MyClass
{
public:
    MyClass()
    {
    //our code goes here
    throw "OMG";//well, some code that throws an exception
    }
};

现在,当发生异常时,正在执行堆栈展开。我还知道,如果从构造函数抛出异常,则不会调用相应对象的析构函数,因为该对象最初从未完全"创建"。

我对此有点困惑。对我来说,这意味着只有当构造函数完成时,对象才被认为是"创建的"。但很明显,所有内存都是在调用构造函数之前(或之后)的某个位置分配的,因为我们可以在构造函数中操作对象的成员。

那么,对象究竟是什么时候在内存中创建的,导致异常的对象的内存会发生什么?

内存是在构造函数主体之前分配的。

如果构造函数失败,则自动分配的内存将被释放

"自动分配"的重音很重要——如果你在构造函数中动态分配了内存,而构造函数失败了(例如,你可能在throw "OMG"之前使用了new),那么这个内存就会泄漏

那是因为-你已经分配了这个内存,你需要释放它。

你是对的,没有调用析构函数,但析构函数不是释放内存的那个,分配给类的auto成员
它(基本上)用于释放内存,由用户(在构造函数或其他地方)分配


换句话说,为对象分配内存与对象的构造不同。

另一个例子——如果你动态地创建一个对象,比如:

MyObj* = new MyObj;

这将:

  • 呼叫operator new
  • 然后调用MyObj的构造函数

看,这两件事不一样。

内存分配对象构造是C++中两个独立的东西。编译器只是确保当它们一起被调用时(这是正则(即非放置)new运算符的情况)并且构造失败时,分配会被恢复。

此外,当类具有子对象(基类和/或字段)时,编译器会按固定顺序调用它们的构造函数,并确保其中一个抛出时,已经构建的子对象得到正确处理(即,调用它们的析构函数)。

对象的内存被释放,但析构函数没有被调用(因此,例如,如果您没有正确处理构造函数中的异常,则在构造函数中创建的动态分配指针不会被释放)。在C++11中,规范规定当且仅当一个构造函数完全完成时调用析构函数(相关,因为构造函数可以引用另一个构造函数)。

示例(new int()分配的内存未释放):

struct Broken {
   int * i;
   Broken () {
      i = new int {5};
      OperationCausingException ();
   }
   ~Broken {
      delete i;
   }
}
try {
   Broken * b = new Broken {};
} catch (...) {
}
// memory leak: *b is freed, but b->i still exists