我能控制静态对象销毁的顺序吗?有没有办法强制指定我的期望顺序?比如说以某种方式指定某个对象最后被销毁,或者至少在另一个静态对象之后被销毁?
我能控制静态对象销毁的顺序吗?有没有办法强制指定我的期望顺序?比如说以某种方式指定某个对象最后被销毁,或者至少在另一个静态对象之后被销毁?
其他答案坚称这是不可能的。根据规范,他们是正确的 - 但是有一个技巧可以让您做到。
只需创建一个包含所有通常要创建为静态变量的内容(例如类或结构体)的单个静态变量即可,如下所示:
class StaticVariables {
public:
StaticVariables(): pvar1(new Var1Type), pvar2(new Var2Type) { };
~StaticVariables();
Var1Type *pvar1;
Var2Type *pvar2;
};
static StaticVariables svars;
在StaticVariables
的构造函数和析构函数中,您可以按任意顺序创建变量,并更重要的是按任意顺序销毁它们。为了使这完全透明,您还可以创建变量的静态引用,例如:
static Var1Type &var1(*svars.var1);
嗨,完全掌控了。 :-) 话虽如此,这需要额外的工作,通常是不必要的。但当它确实需要时,知道这一点非常有用。
静态对象的销毁顺序与构造顺序相反(例如第一个构造的对象最后被销毁),在Meyers的书《Effective C++》的第47项中描述了一种技术,可以控制静态对象的构造顺序,确保在使用之前已经初始化好全局对象。
例如,我如何指定某个对象在其他静态对象之后被销毁。
确保它在另一个静态对象之前被构造。
我如何控制静态对象的构造顺序?并非所有静态对象都在同一个dll中。
为简单起见,我会忽略它们不在同一个DLL中的事实。
根据Meyers的第47项(4页长),我的解释是这样的:假设你的全局变量定义在像这样的头文件中...
//GlobalA.h
extern GlobalA globalA; //declare a global
...在那个包含文件中添加一些代码,就像这样...
//GlobalA.h
extern GlobalA globalA; //declare a global
class InitA
{
static int refCount;
public:
InitA();
~InitA();
};
static InitA initA;
这将导致任何包含GlobalA.h文件的文件(例如,定义第二个全局变量的GlobalB.cpp源文件)都将定义一个InitA类的静态实例。这个实例将在该源文件中的其他任何东西之前被构造(例如,在第二个全局变量之前)。简短回答:一般来说,不行。
稍微详细的回答是:对于单个翻译单元中的全局静态对象,初始化顺序从上到下,销毁顺序完全相反。多个翻译单元之间的顺序是未定义的。
如果你真的需要特定的顺序,你需要自己构造。
在标准的C++中没有办法实现这个,但是如果你对你使用的特定编译器内部有很好的工作知识,那么可能可以实现。
在Visual C++中,指向静态初始化函数的指针位于.CRT$XI
段(用于C类型静态初始化)或.CRT$XC
段(用于C++类型静态初始化)。链接器收集所有声明并按字母顺序合并它们。通过在适当的段中声明对象,您可以控制静态初始化发生的顺序。
#pragma init_seg
#pragma init_seg(".CRT$XCB")
class A{}A;
文件B.cpp:
#pragma init_seg(".CRT$XCC")
class B{}B;
.CRT$XCB
会在.CRT$XCC
之前合并。当CRT迭代静态初始化函数指针时,它会先遇到文件A再遇到文件B。
在Watcom中,该段是XI,变体的#pragma initialize可以控制构建:
#pragma initialize before library
#pragma initialize after library
#pragma initialize before user
...请查看文档以获取更多信息。
你真的需要在 main
函数之前初始化变量吗?
如果不需要,你可以使用一个简单的习惯用语来轻松地控制构造和析构的顺序,详情请看这里:
#include <cassert>
class single {
static single* instance;
public:
static single& get_instance() {
assert(instance != 0);
return *instance;
}
single()
// : normal constructor here
{
assert(instance == 0);
instance = this;
}
~single() {
// normal destructor here
instance = 0;
}
};
single* single::instance = 0;
int real_main(int argc, char** argv) {
//real program here...
//everywhere you need
single::get_instance();
return 0;
}
int main(int argc, char** argv) {
single a;
// other classes made with the same pattern
// since they are auto variables the order of construction
// and destruction is well defined.
return real_main(argc, argv);
}
这并不会阻止你尝试创建该类的第二个实例,但如果你这样做,断言将失败。根据我的经验,它可以正常工作。
不行,你不能这么做。你绝不能依赖于静态对象的构建和销毁顺序。
你可以使用单例来控制全局资源的构建和销毁顺序。
您可以通过使用static std::optional<T>
而不是T
来有效地实现类似的功能。只需像变量一样初始化它,使用间接引用并通过分配std::nullopt
(或对于boost,boost::none
)来销毁它。
与指针不同的是,它具有预分配的内存,这可能是您想要的。因此,如果您销毁它并(可能晚得多)重新创建它,则对象将具有相同的地址(您可以保留该地址),并且您不必在那时支付动态分配/释放的成本。
如果没有std::
/ std::experimental::
,请使用boost::optional<T>
。