Может быть, это легче понять, если подумать о том, что на самом деле делает препроцессор: он копирует содержимое всех включенных файлов заголовков в файл cpp и передает его компилятору.
Теперь предположим, что у вас есть:
// In a.cpp
#include <baseclass.h>
// more code
// In b.cpp
#include <baseclass.h>
// more code
После того, как препроцессор расширит включения, оба файла будут содержать:
int BaseClass::x = 10;
Теперь, как только оба объектных файла будут переданы компоновщику, он дважды увидит символ BaseClass::x
, что является ошибкой.
Теперь, чтобы сделать это еще более очевидным, представьте, что вы поместите это в файл заголовка:
int aGlobalVariable = 10;
А затем включите его в два разных файла cpp, которые оба должны быть связаны в один исполняемый файл. На самом деле он ничем не отличается от вашего примера, если смотреть с точки зрения компоновщика.
Почему это не проблема с объявлениями классов?
Есть разница между объявлениями и определениями. Только последнее вызовет проблемы. Например, все следующие объявления:
extern int a;
void foo(int a);
class Foo { int bar(); };
Принимая во внимание, что это определения:
int a;
int b = 10;
void foo(int a) { /*..*/ }
int Foo::bar() { /*...*/ }
Пока существует одно (и только одно) определение, вы можете иметь столько объявлений, сколько хотите, и компоновщик будет следить за тем, чтобы все они ссылались на одну и ту же функцию или место в памяти.
А что насчет классов? Классы можно только объявлять, а их функции-члены и статические члены должны быть определены. Опять же, каждое определение может существовать только один раз.
Функции-члены и статические члены фактически существуют только один раз в адресном пространстве программы, тогда как обычные члены (переменные экземпляра) существуют для каждого объекта класса.
Возвращаясь к вашей конкретной проблеме: статические члены - это в основном просто глобальные переменные, но привязанные к имени класса.
Надеюсь, это проясняет вам ситуацию!
person
lethal-guitar
schedule
04.04.2014