7

I am making a global singleton that controls my application and I want the subsystems to startup and shutdown in a specific order.

class App
{
public:
    App();
    ~App();

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

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

The constructor creates the pointers in the correct order

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

and I want the unique_ptrs to be deallocated in the reverse order. Does std::unique_ptr have a guarantee that the memory will be deallocated in this order?

I thought about making all of the managers global singletons, but felt this way would be better if I could make it work.

EDIT: It has been brought to my attention that the actual problem is the order that an instance variables members get destructed. In that case is there a guaranteed order for that?

Mike Kinghan
  • 55,740
  • 12
  • 153
  • 182
Xeronate
  • 167
  • 10
  • 12
    This isn't related to any "guarantees" of `unique_ptr`. It is about the order in which data members' destructors get called. – juanchopanza Aug 16 '16 at 16:47
  • 1
    The opposite of their decl order in the class. In your code `renderer` will go first, then `displayManager`. Your smart pointers were *constructed* in class-member declaration order, regardless of how you order them in an initializer list (which you don't have, so default construction reigns) or later change them in the body of your constructor itself. Destruction will be similarly predictable in opposite order. – WhozCraig Aug 16 '16 at 16:52
  • 2
    @WhozCraig: What's this answer doing in the comments section?! – Lightness Races in Orbit Aug 16 '16 at 16:55
  • I think the question title was better before. Now it's _really_ generic and represents a question that has been asked and answered a million times, even though your actual question is a little more specific than that. – Lightness Races in Orbit Aug 16 '16 at 16:56
  • 2
    Note that, if you want to be explicit about the destruction order of the owned objects, you can do that by calling `reset()` on the `unique_ptr`s in the destructor, or wherever else you want to do it. Perhaps that wouldn't be a bad idea even if the current destruction order is working for you. – Benjamin Lindley Aug 16 '16 at 16:59
  • 1
    @LightnessRacesinOrbit because the question has changed several times, and as of when I was going to answer the question, it changed yet-again. As it is now, the title and last sentence are effectively different questions. Also, at this point there is no possible addition such an answer from me could offer that yourself and Nathan haven't already assembled and edited by refinement. It may have been an offering answer at one point, but not anymore. I was hunting for a dupe until the title changed again, then just gave up. – WhozCraig Aug 16 '16 at 17:06
  • @WhozCraig: If you think the question is too confusing to answer then answering at all would seem to be a disservice, let alone answering in a place where your contribution can't be peer reviewed! Note that the question body has not substantially changed _at all_ since it was originally posted. – Lightness Races in Orbit Aug 16 '16 at 17:57

2 Answers2

15

std::unique_ptr doesn't control when it's destructor is called. Instead it is where it is declared that determines what order it is destructed in.

Class members are constructed in the order they are declared in the class body and are destroyed in the reverse of that order. So in your case when a App is constructed displayManager is constructed first and then renderer is constructed. When the App instance is destroyed then renderer is destroyed first and then displayManager is destroyed.


Also note that in

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

You are doing assignment to default constructed unique_ptrs. You need to use the member initialization list like

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>()) {}

If you do not want to default construct the pointers and then assign to them.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • Initializer list was my initial thought, but I was worried about the order of construction. Am I right in thinking the initializer list is initialized in the order the class members are declared in the header rather than the order of the initializer list? – Xeronate Aug 16 '16 at 16:51
  • 1
    @MProgrammer Members are constructed in the order they appear in the class body regardless of how they are initialized in the member initialization list. – NathanOliver Aug 16 '16 at 16:53
  • @MProgrammer: That is correct. A good compiler will warn you if the two differ. – Lightness Races in Orbit Aug 16 '16 at 16:55
  • _"You need to"_ Bit strong? Definitely should, but `op=` will do the job as it is now. – Lightness Races in Orbit Aug 16 '16 at 16:57
  • @LightnessRacesinOrbit You again ;-). It should be better now. – NathanOliver Aug 16 '16 at 17:00
  • Also, if this is basically the PImpl idiom, you can make those `std::unique_ptr`s `const`, to declare that they get constructed holding something but they'll never point to anything else and so the life of the owned data is the life of the `App` instance. – Ben Apr 04 '18 at 14:32
10

Yes, the destruction order is guaranteed.

Each deleter will be invoked as soon as the owing std::unique_ptr is destroyed ref, and the std::unique_ptrs are destroyed in reverse order of their construction when they all go out of scope togetherref.

However, this order has no direct relationship to the order of your assignments inside App::App() — you could swap them around and nothing would change. It's the order of the std::unique_ptrs' declaration inside App that counts.

So, though the destruction order is guaranteed, it may not be the order you were expecting.

Community
  • 1
  • 1
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055