car*_*bir 14 c++ destructor memory-management raii
以相反的顺序为变量分配内存有什么好处?
Arm*_*yan 19
考虑这个例子:
Type1 Object1;
Type2 Object2(Object1);
Run Code Online (Sandbox Code Playgroud)
假设Object2
使用一些内部资源Object1
并且只要Object1
有效就是有效的.例如,Object2
s析构函数访问Object1
的内部资源.如果不是为了保证销毁的逆序,那就会导致问题.
spr*_*aff 12
它不仅仅是解释内存,而是广义上的对称性.
每次创建一个对象时,你都要创建一个新的上下文.你可以根据需要"推"到这些上下文中,然后再"弹回" - 对称是必要的.
当谈到RAII和异常安全时,它是一种非常强大的思维方式,或者证明前提条件和后置条件的正确性(构造函数建立不变量,析构函数应该适用于assert()
它们,并且在精心设计的类中,每个方法都清楚地保留它们).
恕我直言,缺乏这个功能是Java最大的一个缺陷.考虑构造函数打开文件句柄或互斥体或其他任何东西的对象--Alan的答案很好地说明了这种对称如何强制执行某些常识约束(像Java这样的语言可能让Object1在Object2之前超出范围但Object2通过引用计数使Object1保持活动状态)但是从对象生命周期的角度考虑,有很多设计问题可以完全落实到位.
当你牢记这一点时,很多C++陷阱都会解释自己
goto
s不能跨越初始化return
函数(这只适用于非RAII语言,如C和Java)等等......
局部变量的破坏顺序的保证是允许你编写(例如)这样的代码:
{
LockSession s(lock);
std::ofstream output("filename");
// write stuff to output
}
Run Code Online (Sandbox Code Playgroud)
LockSession
是一个在其构造函数中获取锁并在其析构函数中释放它的类.
在}
,我们知道在释放锁之前将关闭(并刷新)文件句柄,这是一个非常有用的保证,如果程序中有其他线程使用相同的锁来保护对同一文件的访问.
假设标准没有指定销毁顺序,那么我们必须担心这个代码释放锁(允许其他线程访问文件)的可能性,然后才设置关于刷新和关闭它.或者为了保证我们需要的保证,我们必须编写这样的代码,而不是:
{
LockSession s(lock);
{
std::ofstream output("filename");
// write stuff to output
} // closes output
} // releases lock
Run Code Online (Sandbox Code Playgroud)
这个例子并不完美 - 不保证刷新文件实际上是成功的,所以依靠ofstream
析构函数来做这件事并不会导致这方面的防弹代码.但即使遇到这个问题,我们至少可以保证在我们发布锁定时我们不再打开文件,而且一般来说这是销毁订单可以提供的有用保证.
在C++中还有其他破坏顺序的保证,例如,在派生类析构函数运行后销毁基类子对象,并且在构造的相反顺序中销毁对象的数据成员,也就是在运行派生类析构函数之后在基类子对象之前.每个保证都在那里,这样你就可以编写代码,以某种方式依赖于第二件事仍然在那里,而第一件事被破坏.
这些都与实际的内存分配无关,它更多的是关于析构函数的作用.既然你问去分配,虽然,有可能是某些情况下,某些内存分配器实现从块的分配相反的顺序被释放受益.它可以使分配器通过合并相邻的空闲块来减少内存碎片更容易一些.但是,你不必经常考虑这一点,无论如何,需要合并空闲块的分配器应该足够聪明,无论它们被分配和释放的顺序如何.
归档时间: |
|
查看次数: |
2988 次 |
最近记录: |