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

Почему статическая переменная должна быть явно определена?

В классе:

class foo
{
public:
    static int bar; //declaration of static data member
};

int foo::bar = 0; //definition of data member

Мы должны явно определить статическую переменную, иначе это приведет к

undefined reference to 'foo::bar'

Мой вопрос:

Почему нам нужно дать явное определение статической переменной?


Обратите внимание, что это НЕ дубликат ранее заданных вопросов undefined reference to static variable. Этот вопрос предполагает задать причину явного определения статической переменной.

4b9b3361

Ответ 1

С начала времен язык C++, как и C, был построен по принципу независимого перевода. Каждая единица перевода составляется самим компилятором самостоятельно, без каких-либо знаний о других единицах перевода. Вся программа собирается только позже, на этапе связывания. Этап компоновки - это самая ранняя стадия, на которой компоновщик видит всю программу (он рассматривается как набор объектных файлов, подготовленных самим компилятором).

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

То же самое относится и к статическим членам класса. Статические члены класса являются объектами с внешней связью. Компилятор ожидает, что вы определите эту сущность в некоторой единице перевода. Вся цель этой функции - дать вам возможность выбрать эту единицу перевода. Компилятор не может выбрать его для вас. Это опять же часть вашего намерения, то, что вы должны сказать компилятору.

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

В дополнение к вышесказанному, на языке C++ точка, в которой вы определяете свою переменную, будет определять порядок ее инициализации относительно других переменных, определенных в той же единице перевода. Это также часть намерений пользователя, то есть то, что компилятор не может решить без вашей помощи.


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

Ответ 2

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

Ввод определения члена static вне класса подчеркивает, что память выделяется только один раз для члена данных static (во время компиляции). Каждый объект этого класса не имеет собственной копии.

Ответ 3

static - это тип хранилища, когда вы объявляете переменную, которую вы сообщаете компилятору, "на этой неделе где-то в разделе данных", и когда вы впоследствии ее используете, компилятор испускает код, который загружает значение из TBD-адреса.

В некоторых контекстах компилятор может управлять тем, что статическая константа является постоянной времени компиляции и заменяет ее такими, например

static const int meaning = 42;

Внутри функции, которая никогда не берет адрес значения.

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

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

Если вы хотите, чтобы мы имели постоянное значение, например

static int MaxEntries;
...
int Foo::MaxEntries = 10;

Вам будет лучше с тем или иным из следующих

static const int MaxEntries = 10;
 // or
enum { MaxEntries = 10 };

Статичность не требует отдельного определения до тех пор, пока что-то не попытается принять адрес или сформировать ссылку на переменную, версия enum никогда не делает.

Ответ 4

Внутри класса вы объявляете только переменную, то есть: вы сообщаете компилятору, что есть что-то с этим именем. Тем не менее, статическая переменная должна получить некоторое пространство памяти для жизни, и это должно быть внутри одной единицы перевода. Компилятор резервирует это пространство только тогда, когда вы ОПРЕДЕЛИТЬ переменную.

Ответ 5

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

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