C++ 中静态对象的销毁顺序

发布于 2024-07-13 00:49:31 字数 84 浏览 13 评论 0原文

我可以控制静态对象被破坏的顺序吗? 有什么办法可以执行我想要的命令吗? 例如,以某种方式指定我希望最后销毁某个对象,或者至少在另一个静态对象之后销毁?

Can I control the order static objects are being destructed?
Is there any way to enforce my desired order? For example to specify in some way that I would like a certain object to be destroyed last, or at least after another static object?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

夏尔 2024-07-20 00:49:31

静态对象以与构造相反的顺序被破坏。 而且施工顺序很难控制。 唯一可以确定的是,在同一个编译单元中定义的两个对象将按照定义的顺序构造。 其他任何事情或多或少都是随机的。

The static objects are destructed in the reverse order of construction. And the order of construction is very hard to control. The only thing you can be sure of is that two objects defined in the same compilation unit will be constructed in the order of definition. Anything else is more or less random.

奢华的一滴泪 2024-07-20 00:49:31

对此的其他答案坚持认为这是不可能完成的。 根据规范,他们是对的 - 但有一个技巧可以让您做到这一点。

仅创建一个类或结构的单个静态变量,其中包含您通常创建静态变量的所有其他内容,如下所示:

class StaticVariables {
    public:
    StaticVariables(): pvar1(new Var1Type), pvar2(new Var2Type) { };
    ~StaticVariables();

    Var1Type *pvar1;
    Var2Type *pvar2;
};

static StaticVariables svars;

您可以按照您需要的任何顺序创建变量,更重要的是,StaticVariables的构造函数和析构函数中按照您需要的任何顺序销毁它们。 为了使其完全透明,您也可以创建对变量的静态引用,如下所示:

static Var1Type &var1(*svars.var1);

Voilà - 完全控制。 :-) 也就是说,这是额外的工作,而且通常是不必要的。 但当有必要时,了解它非常有用。

The other answers to this insist that it can't be done. And they're right, according to the spec -- but there is a trick that will let you do it.

Create only a single static variable, of a class or struct that contains all the other things you would normally make static variables, like so:

class StaticVariables {
    public:
    StaticVariables(): pvar1(new Var1Type), pvar2(new Var2Type) { };
    ~StaticVariables();

    Var1Type *pvar1;
    Var2Type *pvar2;
};

static StaticVariables svars;

You can create the variables in whatever order you need to, and more importantly, destroy them in whatever order you need to, in the constructor and destructor for StaticVariables. To make this completely transparent, you can create static references to the variables too, like so:

static Var1Type &var1(*svars.var1);

Voilà -- total control. :-) That said, this is extra work, and generally unnecessary. But when it is necessary, it's very useful to know about it.

紙鸢 2024-07-20 00:49:31

静态对象的销毁顺序与它们的构造顺序相反(例如,第一个构造的对象最后被销毁),并且您可以使用条款 47 中描述的技术来控制静态对象的构造顺序, Meyers 的《Effective C++》一书中“确保全局对象在使用前已初始化”。

例如,以某种方式指定我希望最后销毁某个对象,或者至少在另一个静态对象之后销毁?

确保它是在其他静态对象之前构造的。

如何控制施工顺序? 并非所有静态数据都在同一个 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 类的静态实例,该实例将在该源文件中的其他任何内容之前(例如,在第二个全局变量之前)构造。

这个 InitA 类有一个静态引用计数器。 当构造第一个 InitA 实例时(现在保证在构造 GlobalB 实例之前),InitA 构造函数可以执行它必须执行的任何操作以确保初始化 globalA 实例。

Static objects are destroyed in the reverse of the order in which they're constructed (e.g. the first-constructed object is destroyed last), and you can control the sequence in which static objects are constructed, by using the technique described in Item 47, "Ensure that global objects are initialized before they're used" in Meyers' book Effective C++.

For example to specify in some way that I would like a certain object to be destroyed last, or at least after another static onject?

Ensure that it's constructed before the other static object.

How can I control the construction order? not all of the statics are in the same dll.

I'll ignore (for simplicity) the fact that they're not in the same DLL.

My paraphrase of Meyers' item 47 (which is 4 pages long) is as follows. Assuming that you global is defined in a header file like this ...

//GlobalA.h
extern GlobalA globalA; //declare a global

... add some code to that include file like this ...

//GlobalA.h
extern GlobalA globalA; //declare a global
class InitA
{
  static int refCount;
public:
  InitA();
  ~InitA();
};
static InitA initA;

The effect of this will be that any file which includes GlobalA.h (for example, your GlobalB.cpp source file which defines your second global variable) will define a static instance of the InitA class, which will be constructed before anything else in that source file (e.g. before your second global variable).

This InitA class has a static reference counter. When the first InitA instance is constructed, which is now guaranteed to be before your GlobalB instance is constructed, the InitA constructor can do whatever it has to do to ensure that the globalA instance is initialized.

蓝眼睛不忧郁 2024-07-20 00:49:31

简短回答:一般来说,不会。

答案稍长:对于单个翻译单元中的全局静态对象,初始化顺序是从上到下的,销毁顺序恰恰相反。 多个翻译单元之间的顺序未定义。

如果您确实需要特定的订单,则需要自己制作。

Short answer: In general, no.

Slightly longer answer: For global static objects in a single translation-unit the initialization order is top to bottom, the destruction order is exactly reverse. The order between several translation-units is undefined.

If you really need a specific order, you need to make this up yourself.

空心↖ 2024-07-20 00:49:31

在标准 C++ 中无法做到这一点,但如果您对特定编译器内部结构有很好的了解,那么它可能是可以实现的。

在 Visual C++ 中,指向静态初始化函数的指针位于 .CRT$XI 段(对于 C 类型静态初始化)或 .CRT$XC 段(对于 C++ 类型) static init) 链接器收集所有声明并按字母顺序合并它们。 您可以通过在适当的段中声明对象来控制静态初始化发生的顺序,

#pragma init_seg

例如,如果您希望在文件 B 之前创建文件 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 initialize 的变体可以控制构造:

#pragma initialize before library
#pragma initialize after library
#pragma initialize before user

...有关更多信息,请参阅文档

Theres no way to do it in standard C++ but if you have a good working knowledge of your specific compiler internals it can probably be achieved.

In Visual C++ the pointers to the static init functions are located in the .CRT$XI segment (for C type static init) or .CRT$XC segment (for C++ type static init) The linker collects all declarations and merges them alphabetically. You can control the order in which static initialization occurs by declaring your objects in the proper segment using

#pragma init_seg

for example, if you want file A's objects to be created before file B's:

File A.cpp:

#pragma init_seg(".CRT$XCB")
class A{}A;

File B.cpp:

#pragma init_seg(".CRT$XCC")
class B{}B;

.CRT$XCB gets merged in before .CRT$XCC. When the CRT iterates through the static init function pointers it will encounter file A before file B.

In Watcom the segment is XI and variations on #pragma initialize can control construction:

#pragma initialize before library
#pragma initialize after library
#pragma initialize before user

...see documentation for more

念三年u 2024-07-20 00:49:31

不,你不能。 您永远不应该依赖静态对象的构造/销毁的另一个。

您始终可以使用单例来控制全局资源的构建/销毁顺序。

No, you can't. You should never rely on the other of construction/destruction of static objects.

You can always use a singleton to control the order of construction/destruction of your global resources.

爱殇璃 2024-07-20 00:49:31

您真的需要在 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);
}

它不会阻止您实际尝试创建该类的第二个实例,但如果您这样做,断言将失败。 根据我的经验,效果很好。

Do you really need the variable to be initialized before main?

If you don't you can use a simple idiom to actually control the order of construction and destruction with ease, see here:

#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);
}

It does not STOP you to actually try to create a second instance of the class, but if you do the assertion will fail. In my experience it works fine.

夏尔 2024-07-20 00:49:31

您可以通过使用 static std::optional 而不是 T 来有效地实现类似的功能。 只需像使用变量一样初始化它,使用间接方法并通过分配 std::nullopt 来销毁它(或者,对于 boost,则为 boost::none )。

它与指针不同,它具有预先分配的内存,我猜这就是您想要的。 因此,如果你摧毁它& (也许很久以后)重新创建它,您的对象将具有相同的地址(您可以保留),并且您当时无需支付动态分配/解除分配的成本。

如果您没有 std:: / std::experimental::,请使用 boost::optional

You can effectively achieve similar functionality by having a static std::optional<T> instead of a T. Just initialize it as you'd do with a variable, use with indirection and destroy it by assigning std::nullopt (or, for boost, boost::none).

It's different from having a pointer in that it has preallocated memory, which is I guess what you want. Therefore, if you destroy it & (perhaps much later) recreate it, your object will have the same address (which you can keep) and you don't pay the cost of dynamic allocation/deallocation at that time.

Use boost::optional<T> if you don't have std:: / std::experimental::.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文