在另一个静态对象的析构函数中构造的静态对象的析构函数

时间:2013-01-11 12:14:27

标签: c++ oop static g++ destructor

我在下一段代码中遇到了一些析构函数问题:

#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");
}

为什么没有为bionic调用foo2的析构函数,而是glibc

修改
仿生的输出:

main 1  
foo1 const  
main 2  
foo1 dest  
foo2 const  
return foo2  

调试信息:

(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]

4 个答案:

答案 0 :(得分:6)

我认为您的代码具有未定义的行为,尽管标准 是不是真的很清楚(或者我找不到它 标准)。你的代码构造了一个新的静态对象 静态对象的析构函数。该标准没有解决 这种情况,但是:

  1. 确实说必须反过来调用析构函数 施工顺序。在你的情况下,这意味着 GetFoo2中的静态对象必须在它之前被破坏 建构,这是自相矛盾的。

  2. §3.6/ 3中的文字描述了析构函数的排序 和atexit注册的功能。要求是 这样每个人都必须使用相同的注册机制。 在您拨打atexit(或从...返回)后拨打exit main)是未定义的行为。

  3. 还有§3.6/ 2,它说“如果一个函数包含 静态或线程存储持续时间的块范围对象 已被破坏,并在该期间调用该函数 使用静态或线程存储破坏对象 持续时间,该程序具有未定的行为,如果流动的 控制通过以前的定义 摧毁了blockcope对象。“这句话谈到了alread 摧毁了物体,但思考并不需要太多想象力 缺乏“尚未构建”的对象只是一个 监督。

  4. 最后,我要说上面的第一点是结论性的 关心意图。在§1.3.24中,有一个注释(非规范性, 但指示意图)“可能会预期未定义的行为 当本国际标准遗漏任何明确的定义时 行为或程序使用错误的构造或 错误的数据。“在这种情况下,唯一的描述 所需的行为是不可能的(因为你不能破坏一个 对象在构建之前),标准什么也没说 关于如何解决这个问题。

答案 1 :(得分:5)

我在此代码中看到的所有实例都是静态的。

结果,在main完成后,它们的析构函数在可执行文件的末尾被调用。

如果没有调用析构函数那么这就是一个错误。

答案 2 :(得分:4)

程序存在时,

静态对象将被销毁。在~Foo2()放置一个断点,您将看到它或将日志写入文件应该可以帮助您诊断它。如果真的没有调用那么它就是编译器错误。

enter image description here

上传图片回答问题很有趣。

答案 3 :(得分:4)

  

C ++ 11 3.6.3 / 1:从main

返回的结果,调用具有静态存储持续时间的初始化对象的析构函数

在程序从main返回时,anotherFoo已初始化;但是foo2没有,因为在GetFoo2销毁期间第一次调用anotherFoo之前,它尚未初始化。因此,对规则的严格解释意味着不应该调用它的析构函数。