Destructor of a static object constructed within t

2019-04-25 09:13发布

问题:

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]

回答1:

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:

  1. 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.

  2. 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.

  3. 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.



回答2:

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.



回答3:

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.



回答4:

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.