Инициализация элементов статических данных
Почему статическая инициализация члена элемента данных вне класса?
class X
{
public:
int normalValue = 5; //NSDMI
static int i;
};
int X::i = 0;
Почему статический член данных (здесь "i" ) является только объявлением, а не определением?
Ответы
Ответ 1
Важно различать инициализатор, который говорит, каково его начальное значение, и определение. Этот модифицированный код действителен, с инициализатором в определении класса:
class X
{
public:
int normalValue = 5;
static const int i = 0; // declaration, with initializer
};
const int X::i; // definition
то есть. То, что должно быть вне класса, - это определение, а не инициализация.
Это потому, что переменная должна иметь адрес в памяти (если только она не используется только в ограниченных ситуациях, например, во временных выражениях времени компиляции).
В объекте, членом которого он является, существует нестатическая переменная-член, поэтому ее адрес зависит от адреса объекта, который его содержит. Каждый раз, когда вы создаете новый X
, вы также создаете новую переменную X::normalValue
. Время жизни нестатических данных начинается с конструктора класса. Синтаксис NSDMI не имеет ничего общего с адресом переменной в памяти, он просто позволяет вам указать начальное значение в одном месте, а не повторять его в каждом конструкторе с явным списком инициализаторов конструктора.
С другой стороны, статическая членная переменная не содержится внутри экземпляра класса, она существует независимо от любого отдельного экземпляра и существует с самого начала программы по фиксированному адресу. Чтобы статическая переменная-член (или любой другой глобальный объект) получала уникальный адрес, компоновщик должен видеть ровно одно определение статической переменной в точно одном объектном файле и назначать ему адрес.
Поскольку для статической переменной требуется точно одно определение только в одном объектном файле, нет смысла разрешать это определение в классе, поскольку определения классов обычно существуют в файлах заголовков и включены в несколько объектных файлов. Поэтому, хотя вы можете предоставить инициализатор в классе, вам все равно нужно определить элемент статических данных.
Вы также можете посмотреть на него, как объявление переменной extern
:
namespace X {
extern int i;
}
Это объявляет переменную, но в программе должно быть определение:
int X::i = 0;
Ответ 2
Вам нужно предоставить отдельное определение для статического члена данных (если его odr-используемый, как определено в С++ 11) просто потому, что это определение должно находиться где-то - в одной и только одной единицы перевода. Элементы данных статического класса - это в основном глобальные объекты (глобальные переменные), объявленные в классе. Компилятор хочет, чтобы вы выбрали конкретную единицу перевода, которая будет содержать фактическое "тело" каждого глобального объекта. Именно вы должны решить, какой блок перевода разместить фактический объект.
Ответ 3
"статический" член класса похож на глобально распределенную переменную (она не связана с экземпляром одного класса), поэтому она должна находиться в каком-то объектном файле (и должна быть объявлена в файле .cpp) в качестве символа как и любая глобальная переменная.
Простой член класса (нестатический) находится в блоке памяти, выделенном для экземпляра класса.
Ответ 4
Простая причина в том, что классы обычно объявляются в заголовочных файлах, которые часто включаются в несколько файлов cpp. Элементы статических данных имеют внешнюю связь и должны быть объявлены ровно одной единицей перевода, что делает их непригодными для определения внутри класса.
Как отмечает juanchopanza, разрешено следующее:
struct A
{
const static int i = 1;
};
Однако это только объявление не определение. Вам все равно нужно определить его, если вы собираетесь использовать адрес i
где-нибудь.
Например:
f(int);
g(int&);
X<A::i> x; // Okay without definition for template arguments
char a[A::i]; // Okay without definition, just using value as constant expression
&A::i; // Need a definition because I'm taking the address
f(A::i); // Okay without definition for pass by value
g(A::i); // Need a definition with pass by reference
Ответ 5
Когда компилятор генерирует двоичный код из блока (экстремальное упрощение: файл cpp и все его включенные заголовки), он выдаст символ для статической переменной и, в конечном счете, код инициализации для этой переменной.
Это нормально для символа статической переменной, который должен быть объявлен в нескольких единицах, но это не нормально для его инициализации несколько раз.
Таким образом, вы должны убедиться, что код инициализации выдается только для одного устройства.
Это означает, что статическая переменная должна быть определена точно в одной единице.
Ответ 6
Имейте в виду, что возможно инициализировать элемент статических данных в точке объявления, если он имеет тип const const типа континуума:
Из стандарта С++ 03, §9.4.2
Если статический член данных имеет тип const const или const, его объявление в классе определение может указывать константный инициализатор, который должен быть интегральным постоянным выражением (5.19)
struct Foo {
static const int j = 42; // OK
};
Ответ 7
Член статических данных
#include<iostream.h>
#include<conio.h>
class static_var
{
static int count; //static member of class
public :
void incr_staticvar()
{
count++;
}
void outputc()
{
cout<<"Value of Static variable Count :- "<<count<<endl;
}
};
int static_function : : count;
void main()
{
clrscr();
static_var obj1,obj2,obj3,obj4;
obj1.incr_staticvar();
obj2.incr_staticvar();
obj3.incr_staticvar();
obj4.incr_staticvar();
cout<<"\nAfter Increment of static variable by Four Different objects is :-\n";
obj1.outputc ( );
obj2.outputc ( );
obj3.outputc ( );
obj4.outputc ( );
getch();
}