为什么c++中的析构函数按与初始化顺序相反的顺序解除内存分配
Why the Destructor in C++ de-allocated memory in reverse order of how they were initialised?
按变量的反向顺序解除内存分配的优势是什么?
考虑这个例子:
Type1 Object1;
Type2 Object2(Object1);
假设Object2
使用了Object1
的一些内部资源,只要Object1
有效,CC_1就有效。例如,Object2
的析构函数访问Object1
的内部资源。如果不是为了保证反销毁顺序,这将会导致问题。
这不仅仅是关于释放内存,这是关于更广泛意义上的对称。
每次创建对象时,都在创建一个要在其中工作的新上下文。当你需要它们时,你"推"进这些上下文,然后"弹出"回来——对称性是必要的。
当涉及到RAII和异常安全,或证明诸如先决条件和后置条件的正确性时,这是一种非常强大的思维方式(构造函数建立不变量,析构函数应该建立不变量,在设计良好的类中,每个方法都清楚地保留它们)。
恕我直言,缺少这个特性是Java最大的缺陷。考虑构造函数打开文件句柄或互锁的对象——Armen的回答很好地说明了这种对称性是如何强制执行一些常言性约束的(像Java这样的语言可能会让Object1在Object2之前离开作用域,但Object2通过引用计数使Object1保持存活),但是当考虑到对象生命周期时,有大量的设计问题可以很好地解决。
当你记住这一点时,很多c++的陷阱就会自己解释了
- 为什么
goto
s不能交叉初始化 - 为什么你可能被建议在任何函数中只有一个
return
(这只适用于非raii语言,如C和Java) - 为什么异常是构造函数失败的唯一合理的方式,同样,为什么析构函数永远不能合理地抛出
- 为什么不应该在构造函数中调用虚函数
等等…
局部变量的销毁顺序保证允许您编写(例如)这样的代码:
{
LockSession s(lock);
std::ofstream output("filename");
// write stuff to output
}
LockSession
是在构造函数中获取锁并在析构函数中释放锁的类。
在}
,我们知道文件句柄将在锁被释放之前被关闭(并刷新),如果程序中有其他线程使用相同的锁来保护对同一文件的访问,这是一个非常有用的保证。
假设标准中没有指定销毁顺序,那么我们必须担心这段代码可能会释放锁(允许其他线程访问文件),然后才开始刷新和关闭它。或者,为了保持我们需要的保证,我们必须这样写代码:
{
LockSession s(lock);
{
std::ofstream output("filename");
// write stuff to output
} // closes output
} // releases lock
这个例子并不完美——刷新文件并不能保证真正成功,所以依靠ofstream
析构函数来完成它并不能在这方面产生防弹的代码。但是,即使存在这个问题,我们至少可以保证在释放锁之前不会再打开文件,通常这是销毁顺序可以提供的有用保证。
c++中还有其他的销毁顺序保证,例如基类的子对象在派生类的析构函数运行之后销毁,对象的数据成员在派生类的析构函数运行之后和基类的子对象之前按相反的构造顺序销毁。每一个保证都在那里,所以你可以在某种程度上写代码,依赖于第二个东西仍然存在,而第一个东西被销毁。
这些都与实际的内存回收没有太大关系,更多的是析构函数的作用。但是,由于您询问了有关取消分配的问题,在某些情况下,可能某些内存分配器实现受益于以与其分配相反的顺序释放块。它可以让分配器更容易地通过合并相邻的空闲块来减少内存碎片。不过,您不需要经常考虑这个问题,无论如何,需要合并空闲块的分配器应该足够聪明,无论它们被分配和释放的顺序如何,它都可以执行。
- 将数组的地址分配给变量并删除
- CMake-按正确顺序将项目与C运行时对象文件链接
- 函数调用中参数的顺序重要吗
- vector.resize()中的分配错误
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 如何按顺序或在指定的地址分配内存?
- 分配时unordered_map更改的顺序
- C++中的结构是否按顺序分配内存?每次都以某种方式获得指针比较的正确答案
- 是静态分配的数组的内存分配始终是地址值的顺序
- 如何将int分解为数字并以相同的顺序为向量分配数字
- SCANF中是否有保证的分配顺序
- SEG故障取决于指针分配的顺序
- 销毁和分配顺序
- 找到最小值,并将其按相同的顺序分配给新变量-C++
- 新的运算符分配函数顺序连续性和初始值
- C++中分配语句的评估顺序
- 为什么c++中的析构函数按与初始化顺序相反的顺序解除内存分配
- 静态分配的构造函数和析构函数顺序
- 顺序'new'运算符是否创建连续分配的内存?
- 删除std::lock_guard相对于其他堆栈分配对象的顺序/速度