8

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]
ecatmur
  • 152,476
  • 27
  • 293
  • 366
Laser
  • 6,652
  • 8
  • 54
  • 85

4 Answers4

6

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.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • I'm less convinced. (1) I think the intent is as if all statics were on a stack, which woukdnt pose a problem for this code. (2) I dont think static destructors use atexit. I think this is well defined behavior. – Mooing Duck Jan 11 '13 at 16:49
  • I may agree on the undefined based on §1.3.24 but that's about it. I see nothing (in n3337) in §3.6 [termination] that addresses the specific case at hand. Verbatim all that is addressed is that if an object is constructed after another, then its destructor shall be called first, however we routinely construct object during the execution of destructors... – Matthieu M. Jan 11 '13 at 17:24
  • I don't see any undefined behavior or contradiction. The lifetime of the Foo1 has ended (since its destructor call started), so the destructor of the Foo2 should run, and it should run after the destructor of the Foo1. – Ville Voutilainen Nov 27 '16 at 21:15
5

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.

Stephane Rolland
  • 38,876
  • 35
  • 121
  • 169
  • 3
    Looks to me like `foo2` will not be constructed until program termination, at the time when `anotherFoo` is destructed. – JasonD Jan 11 '13 at 12:30
  • None of the instances I see should be constructed before `main()`. They're all local statics, and should be constructed the first time the come into scope. – James Kanze Jan 11 '13 at 12:32
  • You bring me some doubts, I'm gonna check about construction of static instances... Let's be sure. – Stephane Rolland Jan 11 '13 at 12:39
  • @JasonD and JamesKanze **You are right**. Cf. http://stackoverflow.com/questions/3063027/when-exactly-is-constructor-of-static-local-object-called . I'm gonna edit my answer accordingly. Thx for pointing me this out. – Stephane Rolland Jan 11 '13 at 12:40
4

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.

enter image description here

And it's fun to upload a pic to answer a question.

billz
  • 44,644
  • 9
  • 83
  • 100
  • It is fun, but when compilers disagree, answers should refer to a higher authority to back up their claims: the Standard. – Matthieu M. Jan 11 '13 at 13:36
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.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • Or that its destructor should be called before the constructor. Or whatever. I think it's a case of undefined behavior. – James Kanze Jan 11 '13 at 12:54
  • @JamesKanze: It certainly doesn't imply that the destructor should be called before the constructor: "Destructors **for initialized objects** [...] are called". – Mike Seymour Jan 11 '13 at 12:55
  • And the object is initialized before you leave the the program. (But I agree that the standard isn't at all clear about the required behavior. Which is why I would argue that it is undefined behavior.) – James Kanze Jan 11 '13 at 12:58
  • 1
    @JamesKanze: "called as a result of **returning from `main`**", (or calling `std::exit`, but that's not relevant here), not as a result of leaving the program. As I said, it's not initialised when the program returns from `main`. – Mike Seymour Jan 11 '13 at 13:01