我在下一段代码中遇到了一些析构函数问题:
#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]
答案 0 :(得分:6)
我认为您的代码具有未定义的行为,尽管标准 是不是真的很清楚(或者我找不到它 标准)。你的代码构造了一个新的静态对象 静态对象的析构函数。该标准没有解决 这种情况,但是:
确实说必须反过来调用析构函数
施工顺序。在你的情况下,这意味着
GetFoo2
中的静态对象必须在它之前被破坏
建构,这是自相矛盾的。
§3.6/ 3中的文字描述了析构函数的排序
和atexit
注册的功能。要求是
这样每个人都必须使用相同的注册机制。
在您拨打atexit
(或从...返回)后拨打exit
main
)是未定义的行为。
还有§3.6/ 2,它说“如果一个函数包含 静态或线程存储持续时间的块范围对象 已被破坏,并在该期间调用该函数 使用静态或线程存储破坏对象 持续时间,该程序具有未定的行为,如果流动的 控制通过以前的定义 摧毁了blockcope对象。“这句话谈到了alread 摧毁了物体,但思考并不需要太多想象力 缺乏“尚未构建”的对象只是一个 监督。
最后,我要说上面的第一点是结论性的 关心意图。在§1.3.24中,有一个注释(非规范性, 但指示意图)“可能会预期未定义的行为 当本国际标准遗漏任何明确的定义时 行为或程序使用错误的构造或 错误的数据。“在这种情况下,唯一的描述 所需的行为是不可能的(因为你不能破坏一个 对象在构建之前),标准什么也没说 关于如何解决这个问题。
答案 1 :(得分:5)
我在此代码中看到的所有实例都是静态的。
结果,在main完成后,它们的析构函数在可执行文件的末尾被调用。
如果没有调用析构函数那么这就是一个错误。
答案 2 :(得分:4)
静态对象将被销毁。在~Foo2()
放置一个断点,您将看到它或将日志写入文件应该可以帮助您诊断它。如果真的没有调用那么它就是编译器错误。
上传图片回答问题很有趣。
答案 3 :(得分:4)
C ++ 11 3.6.3 / 1:从
返回的结果,调用具有静态存储持续时间的初始化对象的析构函数main
在程序从main
返回时,anotherFoo
已初始化;但是foo2
没有,因为在GetFoo2
销毁期间第一次调用anotherFoo
之前,它尚未初始化。因此,对规则的严格解释意味着不应该调用它的析构函数。