I have some problems with destructor, in next code:
#include <stdlib.h>
#include <cstdio>
class Foo2
{
public:
Foo2() { printf("foo2 const\n"); }
~Foo2()
{
printf("foo2 dest\n"); // <--- wasn't called for bionic libc
}
};
static Foo2& GetFoo2()
{
static Foo2 foo2;
printf ("return foo2\n");
return foo2;
}
class Foo1
{
public:
Foo1() { printf("foo1 const\n"); }
~Foo1()
{
printf("foo1 dest\n");
GetFoo2();
}
};
int main( int argc, const char* argv[] )
{
printf("main 1 \n");
static Foo1 anotherFoo;
printf("main 2 \n");
}
Why destructor for foo2 wasn't called for bionic
and was for glibc
?
EDIT
Output for bionic:
main 1
foo1 const
main 2
foo1 dest
foo2 const
return foo2
Debug info:
(gdb) break 22
Breakpoint 1 at 0x8048858: file test.C, line 22.
(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x08048858 in Foo2::~Foo2() at test.C:22
(gdb) cont
[ exited with code 0]
I think your code has undefined behavior, although the standard
isn't really clear about it (or I can't find it in the
standard). Your code constructs a new static object in the
destructor of a static object. The standard doesn't address
this case, but:
It does say that destructors must be called in the reverse
order of construction. In your case, this would imply that the
static object in GetFoo2
must be destructed before it was
constructed, which is self-contradictory.
The text in §3.6/3 describes the sequencing of destructors
and functions registered with atexit
. The requirements are
such that the same registration mechanism must be used for each.
And calling atexit
once you've called exit
(or returned from
main
) is undefined behavior.
There's also §3.6/2, which says that "If a function contains
a block-scope object of static or thread storage duration
that has been destroyed and the function is called during the
destruction of an object with static or thread storage
duration, the program has undefined behavior if the flow of
control passes through the definition of the previously
destroyed blockscope object." This sentence talks about alread
destroyed objects, but it doesn't take much imagination to think
that the absense of "not yet constructed" objects is just an
oversight.
In the end, I'd say that my first point above is conclusive with
regards to intent. In §1.3.24, there is a note (non normative,
but indicative of intent) "Undefined behavior may be expected
when this International Standard omits any explicit definition of
behavior or when a program uses an erroneous construct or
erroneous data." In this case, the only description of the
required behavior is impossible (since you cannot destruct an
object before it was constructed), and the standard says nothing
about how this should be resolved.
All the instances I see in this code are static.
By consequence their destructor is called at the end of the executable, after main is finished.
if the destructor wasn't called then it was a bug.
static object will be destroyed when program exists. put a break point at ~Foo2()
, you will see it or write log to a file should help you diagnose it. If really not called then it's a compiler bug.
And it's fun to upload a pic to answer a question.
C++11 3.6.3/1: Destructors for initialized objects [...] with static storage duration are called as a result of returning from main
At the point at which the program returns from main
, anotherFoo
has been initialised; but foo2
hasn't, since it is not initialised until the first call to GetFoo2
during the destruction of anotherFoo
. Therefore, a strict interpretation of the rules would imply that its destructor should not be called.