假设我有两个本地对象。当函数返回时,是否保证哪一个将首先超出范围?
例如:
我有一个这样的课程:
class MutexLock
{
/* Automatic unlocking when MutexLock leaves a scope */
public:
MutexLock (Mutex &m) { M.lock(); }
~MutexLock(Mutex &m) { M.unlock(); }
};
这是一个非常常见的技巧,用于在超出范围时自动释放互斥锁。但是如果我在范围内需要两个互斥量呢?
void *func(void *arg)
{
MutexLock m1;
MutexLock m2;
do_work();
} // m1 and m2 will get unlocked here. But in what order? m1 first or m2 first?
这确实不会造成任何死锁。但是可能存在释放资源的顺序对用户有用的实例。在这种情况下,重要的是要明确而不是依赖于析构函数?
此外,编译器在任何情况下都可以延迟销毁吗?我的意思是
func()
{
{
foo f();
} ---------> Can compiler choose to not destroy f here, rather do it at the time when func() is returning.
}
答案 0 :(得分:10)
// m1 and m2 will get unlocked here. But in what order? m1 first or m2 first?
将以与构造相反的顺序调用析构函数:m2
然后m1
。
在这种情况下,重要的是要明确而不是依赖于析构函数?
破坏的顺序是明确的,所以你可以依赖它。
此外,编译器在任何情况下都可以延迟破坏吗?
没有。如果确实如此,那将会破坏很多基于RAII的代码(您的MutexLock
类就是一个非常好的例子。)
答案 1 :(得分:4)
在这种情况下,明确而不是依赖析构函数是很重要的吗?
不,这不是必需的。
范围内对象的破坏顺序是明确定义的
它与它们的构造顺序完全相反。
此外,编译器在任何情况下都可以延迟破坏吗?
第
编译器不能,这就是 RAII 的目的。它提供了隐含清洁和机制的机制。在没有任何程序员的明确手动努力的情况下解除分配资源
您延迟销毁的要求与RAII的目的是平行的,它要求手动资源管理。
如果您需要手动资源管理,您可以通过new
在堆上分配指针,并且它们指向的对象将是有效的,除非您直接通过delete
调用和您调用的顺序解除分配它们它们。
当然,也不建议也不鼓励这样做。
答案 2 :(得分:2)
破坏以相反的构造顺序发生:首先m2
然后m1
。
编译器永远不会将对象的生命周期延迟到作用域结束(}
)。
答案 3 :(得分:0)
当对象超出范围时,它总是被销毁 - 这不是java。 f在你指示的地方会被摧毁,并且在func结束时永远不会被销毁。通常,调用析构函数的顺序与它们的构造顺序相反。