Destruction order of static objects in C++

自古美人都是妖i 提交于 2019-11-26 15:22:39

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.

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.

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.

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.

David Mott

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

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.

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.

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::.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!