C ++中静态对象的破坏顺序
我可以控制静态对象被破坏的顺序吗? 有什么办法来执行我想要的命令吗? 例如,以某种方式指定我想要最后销毁某个对象,或者至less在另一个静态对象之后?
静态对象按照与构造相反的顺序被破坏。 而施工的顺序是很难控制的。 唯一可以确定的是在同一个编译单元中定义的两个对象将按照定义的顺序构造。 其他任何东西都或多或less是随机的。
对此的其他答案坚持认为这是不能做到的。 根据规范,他们是正确的 – 但有一个技巧,可以让你做到这一点。
只创build一个类或结构的静态variables,它包含所有其他通常会生成静态variables的东西,如下所示:
class StaticVariables { public: StaticVariables(): pvar1(new Var1Type), pvar2(new Var2Type) { }; ~StaticVariables(); Var1Type *pvar1; Var2Type *pvar2; }; static StaticVariables svars;
你可以按照你需要的顺序创buildvariables,更重要的是,可以按照你需要的顺序在StaticVariables
的构造函数和析构函数中销毁StaticVariables
。 为了使它完全透明,你可以创build静态的variables引用,如下所示:
static Var1Type &var1(*svars.var1);
Voilà – 完全控制。 :-)这就是说,这是额外的工作,而且通常是不必要的。 但是当有必要的时候,了解它是非常有用的。
简短的回答:一般来说,没有。
稍微长一些的答案:对于单个翻译单元中的全局静态对象,初始化顺序是从上到下,销毁顺序完全相反。 几个翻译单元之间的顺序是不确定的。
如果你真的需要一个特定的订单,你需要自己做。
静态对象被破坏的顺序与它们的构造顺序相反(例如,第一个构造的对象最后被销毁),并且可以通过使用第47项中描述的技术来控制静态对象的构build顺序,在Meyers的书“ Effective C ++ ”中, “ 确保全局对象在被使用之前被初始化 ”。
例如,要以某种方式指定我最后要销毁某个对象,或者至less在另一个静态对象之后?
确保它是在另一个静态对象之前构build的。
我怎样才能控制施工秩序? 不是所有的静态都在相同的DLL。
我会忽略(为了简单)它们不在同一个DLL中的事实。
梅耶斯的第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的文件(例如,定义第二个全局variables的GlobalB.cpp源文件)将定义一个InitA类的静态实例,源文件(例如在第二个全局variables之前)。
这个InitA类有一个静态引用计数器。 当第一个InitA实例被构造时,现在保证在你的GlobalB实例被构造之前,InitA构造函数可以做任何事情来确保globalA实例被初始化。
Theres没有办法在标准的C ++中做到这一点,但如果你对你的特定的编译器内部有一个很好的工作知识,它可能可以实现。
在Visual C ++中,指向静态init函数的指针位于.CRT$XI
段(对于Ctypes静态init)或.CRT$XC
段(对于C ++types静态init)。链接器收集所有声明并按字母顺序合并它们。 您可以通过使用正确的段声明对象来控制静态初始化的顺序
#pragma init_seg
例如,如果要在文件B之前创build文件A的对象:
文件A.cpp:
#pragma init_seg(".CRT$XCB") class A{}A;
文件B.cpp:
#pragma init_seg(".CRT$XCC") class B{}B;
.CRT$XCB
在.CRT$XCC
之前被合并。 当CRT迭代通过静态init函数指针时,它将遇到文件B之前的文件A.
在Watcom中,段是XI,#pragma初始化的变体可以控制构造:
#pragma initialize before library #pragma initialize after library #pragma initialize before user
…看到更多的文件
读:
SO初始化顺序
SO解决初始化问题的顺序
不,你不能。 你不应该依赖静态对象的构造/破坏。
您始终可以使用单例来控制构build/销毁全局资源的顺序。
你真的需要在main
之前初始化variables吗?
如果你不这样做,你可以使用一个简单的习惯用法来轻松地控制施工和破坏的顺序,请看这里:
#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); }
它并没有停止你实际尝试创build类的第二个实例,但是如果你做了断言将会失败。 根据我的经验,它工作正常。
通过使用static std::optional<T>
而不是static std::optional<T>
可以有效地实现类似的function。 只要初始化它,就像使用variables一样,使用间接性并通过分配std::nullopt
(或者boost, boost::none
)来销毁它。
它不同于有一个指针,它有预分配的内存,这是我想你想要什么。 因此,如果你摧毁了它(也许晚些时候)重新创build它,那么你的对象将拥有相同的地址(你可以保留),并且当时不支付dynamic分配/释放的成本。
使用boost::optional<T>
如果你没有std::
/ std::experimental::
。