Что значит иметь неопределенную ссылку на статический член?

Я только что написал класс с некоторыми статическими членами данных, но теперь я получаю ошибки о "неопределенных ссылках". Почему это не работает? Что я делаю неправильно?

(Примечание. Предполагается, что это будет вход в FAQ по C++ в Stack Overflow. Если вы хотите критиковать идею предоставления FAQ в этой форме, то публикация в meta, с которой все это началось, будет подходящим местом для этого. этот вопрос отслеживается в чате C++, где идея FAQ возникла в первую очередь, поэтому ваш ответ, скорее всего, будет прочитан теми, кто придумал эту идею.)

2 ответа

Решение

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


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

//In header file
class Example {
    static bool exampleStaticMember;
};

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

Требуемое объявление обычно помещается в файл cpp, который содержит другие определения для членов класса. Он должен находиться в том же пространстве имен, что и определение класса. Определение обычно выглядит так:

//In source file:
//This may optionally have an initialiser (eg "= true")
bool Example::exampleStaticMember; 

Определение может быть помещено в любой файл cpp, но его не следует помещать в заголовок вместе с классом, поскольку это может нарушить правило единого определения.

В особом случае, если статическая переменная-член является константным интегралом или типом перечисления, то в определении класса она может иметь инициализатор:

//In header file
class Example {
    static const int initialised = 15;
};

В этом случае определение в файле cpp все еще требуется, но не разрешено иметь инициализатор:

//In source file
//Note: no initialiser!
const int Example::initialised;

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

Шаблоны

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

//In header file
template<typename T>
class Example {
    static int exampleInt;
    static T exampleT;
}
template<typename T> int Example<T>::exampleInt;
template<typename T> T Example<T>::exampleT;

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

Другие виды использования статики

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

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

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

Вы должны создать экземпляры статических элементов, определенных в заголовке файла.cpp. Например:

// foo.h

class foo {
    static int X;
};


// foo.cpp

#include "foo.h"

int foo::X = 0;
Другие вопросы по тегам