C++ 静态析构函数的阶
如果类C++ 静态析构函数的阶,c++,C++,如果类Foo有一个静态成员变量Bar,我希望Bar的析构函数只在Foo的析构函数的最后一个实例运行之后运行。下面的代码段(gcc 6.3,clang 3.8)不会出现这种情况: 为什么破坏的顺序与建造的顺序不同? 如果 ~()(代码)使用 fo::bar < /Cord> >这是删除后使用的一个.< /p> C++中的对象按顺序排列并按相反顺序销毁。首先是foo构造,然后是bar构造,然后执行main,然后bar被销毁,然后是foo。这就是你看到的行为。之所以出现此开关,是因为foo的构造函数
Foo
有一个静态成员变量Bar
,我希望Bar
的析构函数只在Foo
的析构函数的最后一个实例运行之后运行。下面的代码段(gcc 6.3,clang 3.8)不会出现这种情况:
为什么破坏的顺序与建造的顺序不同?
如果<代码> ~()(代码)使用<代码> fo::bar < /Cord> >这是删除后使用的一个.< /p> C++中的对象按顺序排列并按相反顺序销毁。首先是
foo
构造,然后是bar
构造,然后执行main
,然后bar
被销毁,然后是foo
。这就是你看到的行为。之所以出现此开关,是因为foo
的构造函数不构造foo
,而是构造一个空的unique\u ptr
,因此您无法在输出中看到foo()
。然后用输出构建bar
,在main
中,在Foo
构建很久之后创建实际的Foo
我希望Bar的析构函数只能在Foo的析构函数的最后一个实例运行之后运行
不,作为静态
数据成员,Foo::bar
独立于Foo
的任何实例
对于你展示的代码
static std::unique_ptr<Foo> foo; // no Foo created here
Bar Foo::bar; // Foo::bar is initialized before main(), => "Bar()"
int main(int argc, char **argv) {
foo = std::make_unique<Foo>(); // an instance of Foo is created, => "Foo()"
}
// objects are destroyed in the reverse order how they're declared
// Foo::bar is defined after foo, so it's destroyed at first => "~Bar()"
// foo is destroyed; the instance of Foo managed by it is destroyed too => "~Foo()"
static std::unique\u ptr foo;//这里没有创建Foo
巴福::巴;//Foo::bar在main()之前初始化,=>“bar()”
int main(int argc,字符**argv){
foo=std::make_unique();//创建了一个foo实例,=>“foo()”
}
//对象的销毁顺序与声明顺序相反
//Foo::bar是在Foo之后定义的,因此它首先被销毁=>“~bar()
//福被摧毁;由它管理的Foo实例也被销毁=>“~Foo()”
这里的复杂之处在于,代码没有向foo
的构造函数插入指令。发生的情况是,先构造foo
,然后构造foo::bar
。调用make…unique
构造一个Foo
对象。然后退出main
,两个静态对象按与其构造相反的顺序被销毁:Foo::bar
被销毁,然后Foo
。foo
的析构函数将销毁它所指向的foo
对象,该对象是在main
中创建的静态对象的生存期完全基于其定义的顺序。编译器对何时调用Bar::Bar()
的“了解”不如调用Bar::~Bar()
为了更好地说明这个问题,请考虑这个
class Foo;
struct Bar {
Bar() {
std::cout << "Bar()" << std::endl;
}
~Bar() {
std::cout << "~Bar()" << std::endl;
}
void baz() {}
};
struct Foo {
Foo() {
bar.baz();
std::cout << "Foo()" << std::endl;
}
~Foo() {
std::cout << "~Foo()" << std::endl;
}
static Bar bar;
};
Foo foo;
Bar Foo::bar;
int main() {}
添加std::unique\u ptr
会延迟Foo::Foo()
在其主要构造之后的执行,从而使编译器产生“知道”何时调用Bar::Bar()
的错觉
TLDR静态对象的定义应晚于其依赖项。在定义
bar
之前,定义std::unique_ptr
和定义Foo
同样是一个bug。一般来说,您不应该编写依赖于静态(或全局)数据的构造或销毁顺序的代码。这使得代码无法读取和维护(您可能更喜欢静态智能指针,或者
从main
)调用显式初始化或启动例程。如果链接多个翻译单元,则不会指定顺序
请注意,它提供了and(带优先级)属性。我认为你应该避免使用它们。但是,\uuu属性(构造函数)
对于插件初始化非常有用
在某些情况下,您还可以使用(至少在POSIX系统上)。我不知道这类注册函数是在析构函数之前还是之后调用的(我认为您不应该关心这个顺序)。那么,全局函数是在
main()
实际退出之前销毁的?看起来不奇怪吗?如果Foo
实例使用static Bar
数据怎么办?@Alex它们在main()
之后被销毁了?让我更清楚一点:~Foo()
理论上可以使用static Bar
@Alex-这就是我问这个问题的原因。@Alex然后你会得到一个销毁的对象。不要使用全局对象是个好主意;或者将Foo::bar
的定义移到前面。是的-使用unique\u ptr
是为了模仿我在更复杂的应用程序中的行为。因此,使用析构函数中的静态类成员是否不安全(即在~bar()
中使用Foo::bar
?听起来很疯狂。@我们的一个解决方法是使用static std::unique\u ptr Foo=std::make_unique();
在全局foo
声明中。另一个是在main
的末尾写foo=nullptr;
。如果没有帮助,请解释您想要实现什么。感谢您的回答。代码片段是一个小示例-在我的应用程序foo()中
需要来自argv
的参数,并且还需要传递信号,因此是全局的。您确定该标准规定了某些特定的顺序,尤其是几个翻译单元吗?@basilestrynkevitch不同的翻译单元没有顺序,因此这不适用。具有标准中的适当引号。Have您试图使~Foo()成为
使用条
数据?是的,这就是我提出这个问题的原因。它不会改变行为,并导致使用已销毁的对象。语言中没有任何机制可以确保在所有类实例被销毁后发生某些事情。类不会维护其实例或任何类似实例的计数器@n、 m.不,但是编译器知道在运行Foo()
之前要构造Foo::bar
(允许在Foo()
中安全使用Foo::bar
——人们希望这种行为也能
static std::unique_ptr<Foo> foo; // no Foo created here
Bar Foo::bar; // Foo::bar is initialized before main(), => "Bar()"
int main(int argc, char **argv) {
foo = std::make_unique<Foo>(); // an instance of Foo is created, => "Foo()"
}
// objects are destroyed in the reverse order how they're declared
// Foo::bar is defined after foo, so it's destroyed at first => "~Bar()"
// foo is destroyed; the instance of Foo managed by it is destroyed too => "~Foo()"
class Foo;
struct Bar {
Bar() {
std::cout << "Bar()" << std::endl;
}
~Bar() {
std::cout << "~Bar()" << std::endl;
}
void baz() {}
};
struct Foo {
Foo() {
bar.baz();
std::cout << "Foo()" << std::endl;
}
~Foo() {
std::cout << "~Foo()" << std::endl;
}
static Bar bar;
};
Foo foo;
Bar Foo::bar;
int main() {}
Foo()
Bar()
~Bar()
~Foo()