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

В чем разница между объявлением и определением переменной в С++?

Мой вопрос связан с изучением Эффективного С++ Скотта Мейерса. В пункте II этой книги написано следующее:

Чтобы ограничить область действия константы классом, вы должны сделать ее членом и, чтобы обеспечить не более одной копии константы, вы должны сделать ее статическим членом.

Это правильно написано. Тогда сразу указывается следующий пример:

class GamePlayer {
private:
    static const int NumTurns = 5;
    int scores[NumTurns];
    ....
  };

Затем записывается следующее, относящееся к приведенному выше примеру:

То, что вы видите выше, - это объявление, а не определение NumTurns.

Мой первый вопрос: в чем смысл этого утверждения?

Сразу после этого упоминается следующее:

Обычно С++ требует, чтобы вы предоставили определение для всего, что вы используете, но исключение составляют константы класса, которые являются статическими и целочисленными (например, целые числа, символы, bools). Пока вы не берете их адрес, вы можете объявить их и использовать их без предоставления определения. Если вы берете адрес константы класса или если ваш компилятор неверно настаивает на определении, даже если вы не принимаете адрес, вы предоставляете отдельное определение следующим образом: const int GamePlayer::Numturns; //definition of NumTurns

Почему теперь это определение, а не объявление?

Я понимаю разницу в контексте функции, но не понимаю ее в контексте регулярной переменной. Кроме того, может кто-то расширять то, что автор означает

... если вы берете адрес константы класса, или если ваш.. часть приведенного выше параграфа?

P.S: Я относительно новичок в С++.

4b9b3361

Ответ 1

Подобно функциям, переменные могут иметь "чисто декларативные" декларации и фактические определения. Вы сбиты с толку, потому что раньше, вероятно, не встречались многие объявления переменных переменных.

int i; // Definition
extern int i, j; // (Re)declares i, and declares j
extern int j = 0; // Defines j (confusing, eh?)

Поскольку вы привыкли к функциям, определения являются объявлениями, но не все объявления являются определениями. §3.1/2 гласит

Объявление - это определение, если [...] он не объявляет статические данные член в определении класса (9.2, 9.4),

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

В технических терминах, если статический член данных (или любой объект, если на то пошло) не используется "odr-used", его не нужно определять. Использование Odr для всех объектов определено в п. 3.2/3:

Переменная x, имя которой отображается как потенциально оцененное выражение ex является odr-используемым ex, если не применить преобразование lvalue-to-rvalue (4.1) до x дает постоянное выражение (5.20), которое не вызывает любые нетривиальные функции и, если x - объект, ex - элемент набор потенциальных результатов выражения e, где либо Преобразование lvalue-to-rvalue (4.1) применяется к e, или e является выражение сброшенного значения (раздел 5).

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

Для некоторого класса A и его статического члена данных i,

class A {
    static const int i = 57; // Declaration, not definition
};

const int A::i; // Definition in namespace scope. Not required per se.

Ответ 2

Здесь указано :

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

Здесь термин "адресуемый объект" означает "экземпляр" статического типа данных const. Нет экземпляра, нет адреса, т.е. Его только объявление.
Обратите внимание, что статические переменные, явно определенные в исходном файле, могут быть связаны правильно.

class X
{
    public:
        static int const s_x = 34;
        static int const s_y;
};

int const X::s_y = 12;

int main()
{
    int const *ip = &X::s_x;    // compiles, but fails to link
    ip = &X::s_y;               // compiles and links correctly
} 

... если вы принимаете адрес константы класса, или если ваша.. часть приведенного выше параграфа?

означает, если такой член используется odr, определение в области пространства имен по-прежнему требуется, но оно не должно иметь инициализатора.

struct X {
    const static int n = 1;
};
const int* p = &X::n; // X::n is odr-used
const int X::n;       // ... so a definition is necessary

Ответ 3

Почему теперь это определение, а не объявление?

Поскольку этот оператор заставляет компилятор генерировать адрес для статической переменной.

Кроме того, может ли кто-то расширить то, что автор означает, "если вы делаете адрес константы класса":

Когда вы указываете указатель на переменную, вы берете ее адрес.

Ответ 4

Короткий ответ: декларация говорит, что "эта вещь существует где-то", и определение заставляет пространство выделяться. В вашем случае вы объявили его статическим и const. Компилятор может быть достаточно умным, чтобы заметить, что если вы когда-либо используете его как значение, он может просто заменить это использование литералом 5. Ему не нужно на самом деле создавать пространство для переменной где-то и заполнить его 5, он может использовать 5 непосредственно во время компиляции. Однако, если вы берете его адрес, компилятор больше не может делать это предположение и теперь должен помещать его в адресное. Компилятору нужно, чтобы он существовал в одной единице перевода (грубо: один файл cpp. См. Также правило определения.), Поэтому теперь вы должны явно объявить его где-нибудь.