Pep*_*lac 8 c++ oop static destructor g++
我在下一个代码中遇到了析构函数的一些问题:
#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");
}
Run Code Online (Sandbox Code Playgroud)
为什么不要求foo2的析构函数bionic
用于glibc
?
仿生的编辑输出:
main 1
foo1 const
main 2
foo1 dest
foo2 const
return foo2
Run Code Online (Sandbox Code Playgroud)
调试信息:
(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]
Run Code Online (Sandbox Code Playgroud)
我认为你的代码有不确定的行为,虽然标准并不是很清楚(或者我在标准中找不到它).您的代码在静态对象的析构函数中构造一个新的静态对象.该标准没有涉及这种情况,但是:
它确实说必须以相反的构造顺序调用析构函数.在你的情况下,这意味着静态对象GetFoo2
必须在构造之前被破坏,这是自相矛盾的.
§3.6/ 3中的文本描述了注册的析构函数和函数的顺序atexit
.要求是必须为每个使用相同的注册机制.并呼吁atexit
一旦你叫exit
(或返回
main
)是未定义的行为.
还有§3.6/ 2,它说"如果一个函数包含一个已被破坏的静态或线程存储持续时间的块范围对象,并且在销毁具有静态或线程存储持续时间的对象期间调用该函数,该程序如果控制流经过先前被破坏的blockcope对象的定义,则具有未定义的行为." 这句话谈论了被破坏的物体,但是认为"尚未建造"物体的缺席只是一种疏忽并不需要太多想象.
最后,我会说我的第一点是关于意图的结论.在§1.3.24中,有一个注释(非规范性,但表示意图)"当本国际标准忽略任何明确的行为定义或程序使用错误的结构或错误数据时,可能会出现未定义的行为." 在这种情况下,对所需行为的唯一描述是不可能的(因为在构造之前不能破坏对象),并且标准没有说明如何解决它.
我在这段代码中看到的所有实例都是静态的.
结果,在main完成后,它们的析构函数在可执行文件的末尾被调用.
如果没有调用析构函数那么它就是一个bug.