Странное поведение статической глобальной переменной
Я знаю, что эта программа не использует статическую переменную соответствующим образом, но показывает, как воспроизвести поведение, которое я видел:
Main.cpp:
int main(){
MyObject* p = new MyObject();
Header::i = 5;
printf("i %i\n", Header::i);
p->update();
return 0;
}
MyObject.cpp:
MyObject::MyObject(){
}
void MyObject::update(){
printf("i %i\n", Header::i);
}
Extern.h:
namespace Header {
static int i;
};
Выход, который я получаю:
i : 5
i : 0
Почему я не получаю 5
для обоих выходов? Откуда этот 0
?
Не могли бы вы объяснить, как работают статические переменные?
Ответы
Ответ 1
Статические переменные имеют внутреннюю связь, которая фактически означает, что они являются локальными для блока компиляции. Поскольку у вас есть статическая переменная, объявленная в заголовке, включенном в 2 исходных файла, у вас в основном есть две различные переменные: одна i
локальная для MyObject.cpp
и другая, другая i
, локальная по main.cpp
Ответ 2
У вас есть одна статическая переменная на единицу перевода, где вы включаете заголовок, потому что статические переменные имеют внутреннюю привязку.
Откуда это 0?
Вы не инициализировали переменную во второй блок перевода, а статические переменные инициализируются нулем, откуда приходит значение 0.
В стандарте (§3.6.2/2):
Переменные со статической продолжительностью хранения (3.7.1) [...] должны быть инициализированы нулями (8.5) до любой другой инициализации. [...]
Ответ 3
У вас есть две переменные i
static int i;
поскольку он имеет внутреннюю связь. Это означает, что каждый блок компиляции, в который был включен соответствующий заголовок, имеет свой собственный объект i, а другие единицы компиляции ничего не знают о presnece этого объекта в этом модуле компиляции.
Если вы удалите спецификатор static
, тогда компоновщик должен выпустить сообщение о том, что переменная определена дважды.
Тот же эффект может быть достигнут, если поместить переменную в неназванное пространство имен в С++ 2011. Например, вместо
namespace Header {
static int i;
};
вы могли написать
namespace {
int i;
};
В этом случае varaible я также имеет внутреннюю связь. Это действительно для С++ 2011.
Ответ 4
Вы не должны ставить статические valiables в файлах заголовков. Это приводит к каждому файлу cpp, который включает этот заголовок, чтобы иметь копию этого статического локального элемента в его блок компиляции.
Что вы можете сделать, это спецификатор внешнего хранилища:
Заголовок:
namespace Header {
extern int i;
}
Cpp:
namespace Header {
int i = 0;
}
Ответ 5
В качестве дополнения к всем ответам. ПОЧЕМУ это случается, уже объяснялось. Однако КАК ее исправить было предложено до сих пор только с использованием статического/внешнего подхода. Это немного похоже на C. Unles вам не нужно использовать заголовок в C-части проекта с C-linkage, вы можете использовать С++.
Итак, IF, вы действительно должны использовать что-то статическое в своем коде.
Либо объявить переменную как член класса:
header.h
MyGlobalVariableHoler
{
public: static int i;
};
main.cpp
// class' statics have has to be initialized, otherwise linker error.
int MyGlobalVariableHoler::i=0;
any_code.cpp
#include <header.h>
MyGlobalVariableHolder::i=4711;
Или используйте синглтон, чтобы избежать явной инициализации
header.h
MyGlobalVariableHolder
{
MyGlobalVariableHolder(){i=0;}
public:
static MyGlobalVariableHolder & instance()
{
static MyGlobalVariableHolder inst;
return inst;
}
int i;
};
any_code.cpp
#include <header.h>
MyGlobalVariableHolder::instance().i=4711;
Ответ 6
Лучше объявить вашу переменную с extern
в вашем файле заголовка, чтобы указать, что она имеет внешнюю связь. В противном случае произойдет вышеуказанное поведение или могут возникнуть потенциальные проблемы компиляции или ссылки.
static int i ; // i has internal linkage
extern int i ; // i has external linkage
Ответ 7
Вы путаетесь с статической переменной уровня класса с статической переменной уровня пространства имен. К обоим доступны квалификация X::y
, добавляя к путанице. Другие объяснили фактическую причину (на уровне компиляции/связи).
Ответ 8
Переменная, которая объявлена как статическая, имеет только область видимости в файле, в котором она объявлена, где в качестве переменной, объявленной без статики, можно получить доступ из других файлов с использованием объявления extern.