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

StackOverflow https://stackoverflow.com/questions/469597

  •  19-08-2019
  •  | 
  •  

Вопрос

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

Это было полезно?

Решение

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

Другие советы

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

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

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

    Var1Type *pvar1;
    Var2Type *pvar2;
};

static StaticVariables svars;

Вы можете создавать переменные в любом порядке, и, что более важно, разрушать их в любом порядке, в конструкторе и деструкторе для StaticVariables.Чтобы сделать это полностью прозрачным, вы также можете создать статические ссылки на переменные, например:

static Var1Type &var1(*svars.var1);

Вуаля – тотальный контроль.:-) Однако это лишняя работа, да и вообще ненужная.Но когда это является необходимо, очень полезно об этом знать.

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

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

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

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

  

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

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

  

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

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

Нет никакого способа сделать это в стандартном C ++, но если вы хорошо разбираетесь во внутренних компонентах вашего компилятора, этого, вероятно, можно достичь.

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

#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 перебирает статические указатели функции init, он встречается с файлом 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::none).

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top