Reputation: 1534
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.
Upvotes: 14
Views: 2643
Reputation: 1
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).
Upvotes: 0
Reputation: 21131
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
Upvotes: 1
Reputation: 76245
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
.
Upvotes: 2
Reputation: 172924
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()"
Upvotes: 3
Reputation: 9991
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.
Upvotes: 11