调用析构函数的顺序和要点

问题描述 投票:0回答:4

假设我有两个本地对象。当函数返回时,是否能保证哪个先走出作用域?

例如:

我有一堂这样的课:

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. 
}
c++ destructor
4个回答
12
投票

// m1 and m2 will get unlocked here. But in what order? m1 first or m2 first?

析构函数将以与构造相反的顺序调用:

m2
,然后
m1

在这种情况下,明确而不是依赖析构函数很重要吗?

明确指定销毁顺序,让您可以信赖。

此外,在任何情况下编译器都可以延迟销毁吗?

不。如果确实如此,那将破坏许多基于 RAII 的代码(您的

MutexLock
类就是一个很好的例子)。


4
投票

在这种情况下,明确而不是依赖析构函数很重要吗?
不,这不是必需的。
范围内对象的销毁顺序是明确定义的。
这与它们的构建顺序完全相反。


此外,在任何情况下编译器都可以延迟销毁吗?
没有。
编译器不能,这就是 RAII 的目的。它提供了一种隐式清理和释放资源的机制,无需程序员进行任何显式的手动操作。
您延迟销毁的要求与 RAII 的目的是平行的,它需要手动资源管理
如果您需要手动资源管理,您可以通过

new
在堆上分配指针,并且它们指向的对象将有效,除非并且直到您通过
delete
调用以及调用它们的顺序显式释放它们。
当然,不建议也不鼓励这样做。


2
投票

破坏按照与构造相反的顺序发生:首先

m2
,然后
m1

编译器永远不能将对象的生命周期延迟到作用域结束之后(

}
)。


0
投票

当对象超出范围时,它总是会被销毁 - 这不是 java.lang.Object 。 f 将在您指定的位置被销毁,并且永远不会在 func 结束时被销毁。一般来说,析构函数的调用顺序与其构造顺序相反。

© www.soinside.com 2019 - 2024. All rights reserved.