是否保证 std::unique_ptr 删除顺序?

Posted

技术标签:

【中文标题】是否保证 std::unique_ptr 删除顺序?【英文标题】:Is std::unique_ptr deletion order guaranteed? 【发布时间】:2016-08-16 16:44:38 【问题描述】:

我正在制作一个控制我的应用程序的全局单例,我希望子系统按特定顺序启动和关闭。

class App

public:
    App();
    ~App();

    void start();
    void run();
    void shutdown();

private:
    std::unique_ptr<DisplayManager> displayManager;
    std::unique_ptr<Renderer> renderer;
;

构造函数以正确的顺序创建指针

App::App()

    displayManager = std::unique_ptr<DisplayManager>(new DisplayManager);
    renderer = std::unique_ptr<Renderer>(new Renderer);

我希望以相反的顺序释放 unique_ptrs。 std::unique_ptr 是否保证内存将按此顺序释放?

我曾想过让所有的 manager 都是全局单例,但觉得如果我能做到这一点会更好。

编辑:我注意到实际问题是实例变量成员被破坏的顺序。在那种情况下,是否有保证的订单?

【问题讨论】:

这与unique_ptr 的任何“保证”无关。这与调用数据成员的析构函数的顺序有关。 与他们在课堂上的 decl 顺序相反。在您的代码中,renderer 将首先出现,然后是 displayManager。你的智能指针是按类成员声明顺序构造的,不管你如何在初始化列表中排序它们(你没有,所以默认构造占主导地位)或稍后在主体中更改它们你的构造函数本身。破坏将以相反的顺序同样可预测。 @WhozCraig:这个答案在 cmets 部分有什么作用?! 请注意,如果您想明确说明所拥有对象的销毁顺序,可以通过在析构函数中的unique_ptrs 上调用reset() 来实现,或者您想要的任何其他位置去做吧。即使当前的销毁命令对您有用,也许这也不是一个坏主意。 @LightnessRacesinOrbit 因为问题已经改变了好几次,当我要回答这个问题时,它又改变了。就像现在一样,标题和最后一句实际上是不同的问题。此外,在这一点上,我不可能提供这样的答案,因为您和 Nathan 还没有通过改进组装和编辑。它可能曾经是一个提供答案,但现在不是了。我一直在寻找一个骗子,直到标题再次改变,然后就放弃了。 【参考方案1】:

std::unique_ptr 不控制何时调用它的析构函数。相反,它是在声明它的地方决定它被破坏的顺序。

类成员按照它们在类主体中声明的顺序构造,并以与该顺序相反的顺序被销毁。因此,在您的情况下,当构造 App 时,首先构造 displayManager,然后构造 renderer。当App 实例被销毁时,renderer 先被销毁,然后displayManager 被销毁。


还要注意在

App::App()

    displayManager = std::unique_ptr<DisplayManager>(new DisplayManager);
    renderer = std::unique_ptr<Renderer>(new Renderer);

您正在对默认构造的unique_ptrs 进行赋值。您需要使用成员初始化列表,如

App::App(): displayManager(new DisplayManager), renderer(new Renderer) 
// or if you want to be in the don't use new camp
App::App(): displayManager(std::make_unique<DisplayManager>()), renderer(std::make_unique<Renderer>()) 

如果您不想默认构造指针然后分配给它们。

【讨论】:

Initializer list 是我最初的想法,但我担心构造的顺序。我是否认为初始化列表是按照类成员在标题中声明的顺序而不是初始化列表的顺序初始化的? @MProgrammer 成员按照它们在类主体中出现的顺序构造,而不管它们在成员初始化列表中是如何初始化的。 @MProgrammer:没错。如果两者不同,一个好的编译器会警告你。 “你需要” 有点强?绝对应该,但op= 会像现在这样完成这项工作。 @LightnessRacesinOrbit 你又来了 ;-)。现在应该好多了。【参考方案2】:

是的,销毁顺序是有保证的。

每个删除器将在所欠的std::unique_ptr 被销毁时立即被调用ref,当std::unique_ptrs 都超出范围时,它们将按照其构造的相反顺序被销毁一起ref.

但是,此顺序与您在 App::App() 中的分配顺序没有直接关系——您可以交换它们,不会有任何改变。重要的是 App 中的 std::unique_ptrs' 声明的顺序。

因此,虽然销毁顺序是有保证的,但它可能不是您期望的顺序。

【讨论】:

是的,我从您的每个帖子中都得到了 2 个独立的要点。谢谢。

以上是关于是否保证 std::unique_ptr 删除顺序?的主要内容,如果未能解决你的问题,请参考以下文章

错误:使用已删除的函数‘std::unique_ptr<...> [关闭]

如何在构造函数中使用删除器初始化 std::unique_ptr?

C++ 使用带有基类的已删除函数 std::unique_ptr

具有右值删除器的 unique_ptr 构造函数返回 null?

双向链表 std::unique_ptr 类在节点删除时无法按预期工作

一些 std::unique_ptr 使用和“陷阱”