Порядок уничтожения статических объектов в C ++ - PullRequest
48 голосов
/ 22 января 2009

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

Ответы [ 9 ]

50 голосов
/ 22 января 2009

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

23 голосов
/ 22 января 2009

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

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

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

    Var1Type *pvar1;
    Var2Type *pvar2;
};

static StaticVariables svars;

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

static Var1Type &var1(*svars.var1);

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

11 голосов
/ 22 января 2009

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

11 голосов
/ 22 января 2009

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

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

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

4 голосов
/ 05 мая 2010

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

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

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

... подробнее см. Документацию

3 голосов
0 голосов
/ 25 ноября 2016

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

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

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

0 голосов
/ 18 февраля 2015

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

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

0 голосов
/ 22 января 2009

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

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

...