14

If a class Foo has a static member variable Bar, I would expect Bar's destructor to run only after the last instance of Foo's destructor runs. This doesn't happen with the code snippet below (gcc 6.3, clang 3.8):

#include <memory>
#include <iostream>

class Foo;
static std::unique_ptr<Foo> foo;

struct Bar {
    Bar() {
        std::cout << "Bar()" << std::endl;
    }

    ~Bar() {
        std::cout << "~Bar()" << std::endl;
    }
};

struct Foo {
    Foo() {
        std::cout << "Foo()" << std::endl;
    }

    ~Foo() {
        std::cout << "~Foo()" << std::endl;
    }

    static Bar bar;
};
Bar Foo::bar;

int main(int argc, char **argv) {
    foo = std::make_unique<Foo>();
}

Outputs:

Bar()
Foo()
~Bar()
~Foo()

Why is the order of destruction not the reverse of construction here? If ~Foo() uses Foo::bar this is a use after delete.

rthur
  • 1,466
  • 1
  • 15
  • 15
  • Have you tried to make the `~Foo()` use the `bar` data? – Alex Nov 23 '17 at 15:12
  • Yes, that's what led me to asking the question. It doesn't change the behavior, and leads to using a destroyed object. – rthur Nov 23 '17 at 15:13
  • 1
    There's no mechanism in the language which would ensure that something happens after all class instances are destroyed. A class doesn't maintain a counter of its instances or anything similar. – n. m. could be an AI Nov 23 '17 at 15:41
  • @n.m. No, but the compiler knows to construct `Foo::bar` before `Foo()` is run (allowing safe usage of `Foo::bar` in `Foo()` - one would hope this behavior would also apply to `~Foo()`. – rthur Nov 23 '17 at 15:47
  • 1
    The compiler knows only about static objects, and their destruction order has nothing to do with their types. – n. m. could be an AI Nov 23 '17 at 16:19
  • Well, I misunderstood a lot. [Static members __aren't__ guaranteed to be constructed before its first use](http://coliru.stacked-crooked.com/a/ff3fa11f42255349). @rthur No, it is simply the order in which you wrote them. Static members looks broken as hell though. – Passer By Nov 23 '17 at 16:42
  • @PasserBy That's scary.. Replacing the static member with a [function with a static variable seems to work well enough though](http://coliru.stacked-crooked.com/a/f24bba0bec849fe8) – rthur Nov 23 '17 at 17:55
  • Even though they both have `static`, they are different things. – Passer By Nov 23 '17 at 17:56
  • Yes, but the end result is pretty much the same - a single instance per class. – rthur Nov 23 '17 at 18:04

5 Answers5

11

In C++ the objects are constructed in order of occurrence and destructed in the reverse order. First comes foo construction, then bar construction then main is executed then bar is destructed and then foo. This is the behavior you are seeing. The switch appears because the constructor of foo doesn't construct a Foo, it constructs an empty unique_ptr, so you don't get to see Foo() in the output. Then bar is constructed with the output and in main you create the actual Foo after foo has long been constructed.

nwp
  • 9,623
  • 5
  • 38
  • 68
  • Yes - using a `unique_ptr` is to mimic the behavior I have in a more complicated application. Is it therefore unsafe to use a static class member from a destructor (ie using `Foo::bar` in `~Bar()`? That sounds crazy. – rthur Nov 23 '17 at 15:09
  • @rthur One fix is to use `static std::unique_ptr foo = std::make_unique();` in the global `foo` declaration. Another is to write `foo = nullptr;` at the end of `main`. If that doesn't help please explain what you want to achieve. – nwp Nov 23 '17 at 15:13
  • Thanks for the answer. The code snippet is a small example - in my application `Foo()` requires parameters from `argv`, and also need to be passed signals, hence being a global. – rthur Nov 23 '17 at 15:18
  • Are you sure that the *standard* mandates some particular order, notably with several translation units? – Basile Starynkevitch Nov 23 '17 at 19:10
  • @BasileStarynkevitch Different translation units don't have an order, so this doesn't apply. [This answer](https://stackoverflow.com/a/2204628/3484570) has appropriate quotes from the standard. – nwp Nov 23 '17 at 19:18
3

I would expect Bar's destructor to run only after the last instance of Foo's destructor runs.

No, as a static data member, Foo::bar is independent of any instances of Foo.

And for the code you showed,

static std::unique_ptr<Foo> foo; // no Foo created here

Bar Foo::bar; // Foo::bar is initialized before main(), => "Bar()"

int main(int argc, char **argv) {
    foo = std::make_unique<Foo>(); // an instance of Foo is created, => "Foo()"
} 

// objects are destroyed in the reverse order how they're declared
// Foo::bar is defined after foo, so it's destroyed at first => "~Bar()"
// foo is destroyed; the instance of Foo managed by it is destroyed too => "~Foo()"
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • So, globals are destroyed before `main()` actually exits? Doesn't it look strange? What if `Foo` instance uses the `static Bar bar` data? – Alex Nov 23 '17 at 15:03
  • @Alex They're destroyed after `main()` ? – songyuanyao Nov 23 '17 at 15:08
  • 2
    Let me be more clear: `~Foo()` theoretically may use `static Bar bar`. – Alex Nov 23 '17 at 15:09
  • @Alex Exactly - that's what led me to asking this question. – rthur Nov 23 '17 at 15:11
  • @Alex Then you'll get a destroyed object. Don't use global objects is a good idea; or move the definition of `Foo::bar` to the front. – songyuanyao Nov 23 '17 at 15:12
  • Actually I missed the static pointer, so yes, using the globals that are dependent on each other is generally not good idea. – Alex Nov 23 '17 at 15:14
2

The complication here is that the code doesn’t instrument the constructor of foo. What happens is that foo gets constructed first, than Foo::bar gets constructed. The call to make…unique constructs a Foo object. Then main exits, and the two static objects get destroyed in reverse order of their construction: Foo::bar gets destroyed, then foo. The destructor for foo destroys the Foo object that it points to, which is the one created inmain.

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
  • `foo` doesn't seem to get constructed first though - `Foo::bar` is according to the output. – rthur Nov 23 '17 at 15:22
  • @rthur — the constructor for `foo` doesn’t write any output. After its construction it holds a null pointer. – Pete Becker Nov 23 '17 at 15:57
  • I might be missing something - `Foo()` prints out `Foo()`. Even so, would using `Foo::bar` in `Foo()` therefore be undefined behavior? – rthur Nov 23 '17 at 15:58
  • @rthur — the type of `foo` is `std::unique_ptr`. Its default constructor does not create a `Foo` object. It holds a null pointer. – Pete Becker Nov 23 '17 at 16:03
  • Ah yes sorry - I thought we were talking about when the `Foo` object is constructed, not the `foo` pointer. That explains the order of construction / destruction - thanks! – rthur Nov 23 '17 at 16:16
1

Static objects' lifetimes are based solely on the order of their definition. The compiler doesn't "know enough" when to call Bar::Bar() as much as calling Bar::~Bar().

To illustrate the problem better, consider this

class Foo;

struct Bar {
    Bar() {
        std::cout << "Bar()" << std::endl;
    }

    ~Bar() {
        std::cout << "~Bar()" << std::endl;
    }

    void baz() {}
};

struct Foo {
    Foo() {
        bar.baz();
        std::cout << "Foo()" << std::endl;
    }

    ~Foo() {
        std::cout << "~Foo()" << std::endl;
    }

    static Bar bar;
};

Foo foo;
Bar Foo::bar;

int main() {}

Prints

Foo()
Bar()
~Bar()
~Foo()

The addition of std::unique_ptr postpones Foo::Foo() after its construction in main, giving the illusion of the compiler "knowing" when to call Bar::Bar().

TLDR Static objects should be defined later than its dependencies. Before defining bar, it is just as much a bug to define a std::unique_ptr<Foo> and to define a Foo

Passer By
  • 19,325
  • 6
  • 49
  • 96
0

In general, you should not write code which depends on the order of construction or destruction of static (or global) data. This makes unreadable and unmaintainable code (you might prefer static smart pointers, or explicit initialization or startup routines called from main). And AFAIK that order is not specified when you link several translation units.

Notice that GCC provides the init_priority and constructor (with priority) attributes. I believe you should rather avoid using them. However, the __attribute__(constructor) is useful inside plugins, for plugin initialization.

In some cases, you might also use atexit(3) (at least on POSIX systems). I don't know if such registered functions are called before or after destructors (and I think you should not care about that order).

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547