Каков наилучший способ объявить глобальную переменную? - PullRequest
5 голосов
/ 11 марта 2009

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

Я обычно использую объявление и определение в файле cpp, а затем использую extern в другом файле cpp (а не в заголовках).

Мне не нравится этот подход, и я рассматриваю что-то вроде этого:

В заголовочном файле:

some_file.h

Class MYGlobalClass
{

};


MyGlobalClass& MyGlobalClassInstance()
{
   static MYGlobalClass instance; 
   return  instance;

}

Редактировать

Рассмотрим в следующих контекстах:

  • может использоваться в многопоточных приложениях
  • загрязнение пространства имен
  • НЕ ДОЛЖНО быть одноэлементным, так как может быть создано много экземпляров

Каковы ваши мысли, предложения, новые идеи?

Ответы [ 8 ]

10 голосов
/ 11 марта 2009

Лучший совет, вероятно, "старайтесь избегать глобалов". Людям не нужны глобальные переменные так часто, как они думают. Обычно оказывается, что «передать все в качестве аргументов конструкторам» не так много работы, как люди думают, когда слышат предложение. Это также приводит к более чистому коду с меньшим количеством и более явными зависимостями.

Мне неизвестен какой-либо «правильный» способ объявления глобалов в C ++. То, как вы это делаете, теперь работает нормально, но порядок инициализации не определен, поэтому, если есть какие-либо зависимости между вашими глобальными переменными, у вас проблемы.

Функция, возвращающая статический экземпляр, более или менее решает эту проблему, но не является поточно-ориентированной.

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

4 голосов
/ 11 марта 2009

Объявите как extern в одном заголовочном файле, включенном в «many», и определите его в одном * .cpp файле

2 голосов
/ 11 марта 2009

Объявите его в одном заголовочном файле (используя extern) и определите его в одном .cpp (или любом другом расширении) файле. Вы можете использовать функцию и возвращать ссылку на статическую переменную, как вы показали, чтобы обойти проблемы с порядком построения относительно других таких переменных области видимости пространства имен в других .cpp файлах. Но помните, что это не защитит вас от проблем порядка уничтожения - что в точном обратном порядке по сравнению с конструкцией (эти вещи называются «фиаско статического порядка инициализации». Если вы используете функцию, подобную вашей, и помещаете ее в заголовки, сделайте ее встроенной сделать переопределение функции действительным, когда оно включено в несколько файлов .cpp (логически, функция все еще видна только один раз, потому что статическое в ней будет существовать только один раз, а не отдельно для каждого файла, в который она включена). В качестве альтернативы просто объявите его в заголовке, но определите его в одном .cpp файле (но затем удалите встроенный из него!).

inline A& getA() { static A a; return a; }

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

inline A& getA() { static A *a = new A; return *a; }

Однако его деструктор никогда не будет вызван. Если вам нужна безопасность потоков, вы должны добавить мьютекс, который защищает от множественного доступа. boost.thread вероятно, есть что-то для этого.

2 голосов
/ 11 марта 2009

Ваше представление о статическом внутри функции доступа значительно отличается от глобальной переменной. Разница заключается в том, когда он создается, и, скорее всего, это будет серьезной проблемой с несколькими потоками. Что если два потока вызывают MyGlobalClassInstance одновременно? В зависимости от среды, но я подозреваю, что это типично для большинства компиляторов C ++, вы потенциально можете получить два вызова к конструктору MyGlobalClass, работающему в одно и то же время, обращаясь к той же области памяти.

Если вы однопоточные, это менее вероятно, будет проблемой.

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

1 голос
/ 11 марта 2009

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

// MyClass.h
class MyClass { ... };
extern MyClass myGlobalInstance;

// MyClass.cpp
MyClass myGlobalInstance;

Если это просто глобальный объект, к которому действительно должен обращаться только один модуль, ограничьте его область действия, либо сделав его частной (или защищенной) переменной статического класса, статической переменной функции (если она требуется только одной функции) или в анонимном пространстве имен:

Вариант 1:

// MyClass.h
class MyClass
{
private:  // or protected, if you want it accessible by subclasses
    static MyClass myGlobalInstance;
};

Вариант 2:

// MyClass.cpp
void someFunction()
{
    // it's global, but only accessible inside this function
    static MyClass myGlobalInstance;
    ...
}

Вариант 3:

// MyClass.cpp
namespace
{
    MyClass myGlobalInstance;
}

// it's now only accessible in this file
1 голос
/ 11 марта 2009

объявить и определить в файле cpp

Храните объявление extern в заголовке. Определите его только один раз в файле реализации.

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

namespace myns {
   int foo = 0;
}

Теперь, если это объект класса, вы смотрите на шаблон Singletion. Фактически, ваш пример кода отражает дизайн Singleton. Однако, если вы собираетесь определить функцию в заголовке, сделайте ее встроенной - иначе ODR нарушит.

0 голосов
/ 11 марта 2009

Почему бы не использовать старый добрый синглтон?

0 голосов
/ 11 марта 2009

extern MyGlobalClass MyGlobalClassInstance;

Редактировать: не статично>. <</p>

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...