Подтвердить что ты не робот

Порядок уничтожения статических объектов в С++

Могу ли я контролировать порядок статических объектов, которые разрушаются? Есть ли способ обеспечить соблюдение моего желаемого порядка? Например, чтобы каким-то образом указать, что я хотел бы, чтобы какой-то объект был уничтожен последним или, по крайней мере, после другого статического объекта?

4b9b3361

Ответ 1

Статические объекты разрушаются в порядке, обратном порядку. И порядок строительства очень трудно контролировать. Единственное, что вы можете быть уверены в том, что два объекта, определенные в одном модуле компиляции, будут построены в порядке определения. Все более или менее случайное.

Ответ 2

Другие ответы на это настаивают на том, что это невозможно. И они правы, согласно спецификации, но есть трюк, который позволит вам это сделать.

Создайте только одну статическую переменную, класса или структуры, которая содержит все другие вещи, которые вы обычно делаете статические переменные, например:

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à - полный контроль.:-) Это говорит о том, что это дополнительная работа и вообще не нужна. Но когда это необходимо, очень полезно знать об этом.

Ответ 3

Короткий ответ: В общем, нет.

Немного более длинный ответ: для глобальных статических объектов в одной трансляционной единице порядок инициализации сверху вниз, порядок уничтожения в точности обратный. Порядок между несколькими единицами перевода undefined.

Если вам действительно нужен конкретный заказ, вам нужно сделать это самостоятельно.

Ответ 4

Статические объекты уничтожаются в обратном порядке, в котором они сконструированы (например, первый построенный объект уничтожен последним), и вы можете управлять последовательностью, в которой статические объекты создаются, используя метод, описанный в Пункт 47, " Убедитесь, что глобальные объекты инициализированы до их использования" в книге Мейерса "Эффективный С++".

Например, чтобы каким-то образом указать, что я хотел бы, чтобы какой-то объект был уничтожен последним или, по крайней мере, после другого статического onject?

Убедитесь, что он создан перед другим статическим объектом.

Как я могу контролировать порядок строительства? не все статики находятся в одной и той же 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(например, ваш исходный файл GlobalB.cpp, который определяет вашу вторую глобальную переменную), будет определять статический экземпляр класса InitA, который будет создан до всего else в этом исходном файле (например, перед вашей второй глобальной переменной).

Этот класс InitA имеет статический счетчик ссылок. Когда будет сконструирован первый экземпляр InitA, который теперь гарантированно будет создан до создания экземпляра GlobalB, конструктор InitA может сделать все, что он должен сделать, чтобы гарантировать, что экземпляр globalA инициализирован.

Ответ 5

Невозможно сделать это в стандартном С++, но если у вас есть хорошее знание ваших специфических внутренних компонентов компилятора, возможно, это возможно.

В Visual С++ указатели на статические функции инициализации расположены в сегменте .CRT$XI (для статического init типа C) или .CRT$XC сегмент (для С++ типа static init). Компилятор собирает все объявления и объединяет их в алфавитном порядке. Вы можете управлять порядком, в котором происходит статическая инициализация, объявляя ваши объекты в соответствующем сегменте, используя

#pragma init_seg

например, если вы хотите создать объекты A перед файлом B:

Файл 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 выполняет итерацию с помощью статических указателей функции инициализации, он будет сталкиваться с файлом A перед файлом B.

В Watcom сегмент XI, а варианты #pragma initialize могут управлять конструкцией:

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

... см. документацию для более

Ответ 7

Нет, вы не можете. Вы никогда не должны полагаться на другое из конструкции/уничтожения статических объектов.

Вы всегда можете использовать синглтон для управления порядком построения/уничтожения ваших глобальных ресурсов.

Ответ 8

Вам действительно нужна переменная, которая должна быть инициализирована до 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);
}

Это не останавливает вас, чтобы на самом деле попытаться создать второй экземпляр класса, но если вы сделаете это утверждение, это не удастся. По моему опыту, он отлично работает.

Ответ 9

Вы можете эффективно достичь аналогичной функциональности, имея static std::optional<T> вместо T. Просто инициализируйте его так же, как и с переменной, используйте его с косвенным движением и уничтожьте его, назначив std::nullopt (или, для boost, boost::none).

Он отличается от указателя на то, что он имеет предварительно распределенную память, и я думаю, что вы хотите. Поэтому, если вы уничтожите его и, возможно, намного позже, его воссоздайте, ваш объект будет иметь тот же адрес (который вы можете сохранить), и вы не платите за динамическое распределение/освобождение в это время.

Используйте boost::optional<T>, если у вас нет std::/std::experimental::.