本节书摘来自异步社区出版社《Imperfect C++中文版》一书中的第2章,第2.1节,作者: 【美】Matthew Wilson,更多章节内容可以访问云栖社区“异步社区”公众号查看。
2.1 对象生命周期
Imperfect C++中文版
每个C++对象的生命周期都分为4段:不存在、部分构造、实例化、部分析构[Stro1997]。此外,一个对象所占用的空间必须在该对象构造之前就分配好,并且在该对象析构之后被释放。1
对象可以以下例4种标准方式诞生。
全局对象,包括真正的“全局”对象、位于名字空间中的对象,以及作为类静态成员的对象,生存于任何函数的作用域之外。它们通常在main()函数被执行之前就已经构造完毕,并且在main()结束之后被自动销毁(见11.1节)。它们所占用的空间是由编译器/连接器分配的。
栈对象生存在局部执行框架上,位于函数中。它们在声明点上被创建(其声明点也是定义点),并在超出作用域时被自动销毁。它们所占用的内存空间是通过调整栈指针而分配的,由编译器/连接器所决定。
堆对象生存于堆/自由存储区上[Stro1997]。它们使用new操作符进行创建,并且通过显式调用delete操作符而销毁。它们所占用的空间来自自由存储区,而自由存储区可能会出现空间不够用的情况,从而导致新建对象失败(参见32.2节,以便了解关于这种情况的后果以及如何处理的详细讨论)。C++语言基础设施确保内存分配以及对构造函数的调用会连在一起进行,2并且确保对析构函数的调用以及内存释放也会连在一起进行。3
作为另一个对象的一部分。在这种情况下,该子对象的生命期依赖于外围对象的生命期(见第5章)。
就地构造
除了标准途径外,你还可以通过placement new(定位new)和显式析构来明确地控制对象所占的内存和生命期:
byte_t *p = . . . // 满足SomeClass的对齐要求的内存块
SomeClass &sc = *new(p) SomeClass(); // 创建实例
. . .
sc.SomeMethod();
. . .
sc.~SomeClass(); // 显式地销毁实例,p指向的内存块依然存在
毫无疑问,这不是好的编码习惯。姑且不管容器的实现(容器是按值而不是按引用存储的),很少有合理的理由来使用该技术。
1从概念上说是这样。不过前一个对象所占用的内存在该对象析构之后可以被再次使用,其间没有任何实质上的内存释放,例如这在STL容器中就比较常见。
2译者注:即调用new操作符的时候,不但分配内存,还会调用相应的构造函数。
3译者注:即delete会先调用相应的析构函数再释放内存。
本文仅用于学习和交流目的,不代表异步社区观点。非商业转载请注明作译者、出处,并保留本文的原始链接。