2011-09-25 4 views
2

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

class Something 
{ 
    public: 
    static int s_nValue; 
}; 

int Something::s_nValue = 1; 

int main()  
{  
    Something::s_nValue = 2;  
    std::cout << Something::s_nValue;  
    return 0;  
} 

Мой вопрос: мы объявили s_nvalue в классе уже есть, и почему это необходимо пересмотреть его еще раз? Если мы еще не напишем int, появится сообщение об ошибке. Почему это так?

+3

Потому что это то, чего хочет язык. –

+2

возможный дубликат [статической членной переменной C++ и ее инициализацией] (http://stackoverflow.com/questions/4547660/c-static-member-variable-and-its-initialization) –

ответ

2

В обычной программе на C++ ваши классы определены в заголовках, которые включаются всеми исходными файлами, которые их используют. Поэтому, если это сработает так, как вы ожидаете, каждый исходный файл будет иметь свою собственную копию этой статической переменной, когда на самом деле они должны использовать одну копию. Это было бы нарушением правила одного определения ... каждый объект может быть определен только как существующий в одном месте.

Итак, объявляя переменную в классе, просто объявляет компилятору, что где-то будет переменная с этим именем и типом; он не инструктирует компилятор выделить место для него. На этом этапе переменная остается неопределенной в любых исходных файлах, которые ее включают. Затем в одном конкретном исходном файле [обычно файл реализации для этого конкретного класса] вы предоставляете фактическое определение, int Something::s_nValue;. Это просит компилятор выделить пространство для переменной, так что он существует только в этом месте, и нет никакой двусмысленности, когда вы идете связать все ваши объектные файлы вместе.

2

Это характер C++, когда вы определяете что-то, что вам нужно указать, это точный тип, даже если у вас было объявление ранее. Это справедливо для всех переменных и функций.

Sidenote: вы не переопределяете его, что вызовет ошибку компиляции. Вы просто определяете это.

1

Добро пожаловать в чудесный мир C++: объявления VS. определения. В опубликованном вами коде есть декларация и определение. A Объявление дает некоторый символ имя и тип. A определение дает символ «значение».

class Something 
{ 
public: 
    // declaration. 
    static int s_nValue; 
}; 

// definition. 
int Something::s_nValue = 1; 

Этот процесс похож функционировать прототипирования:

// declaration. 
void f (int i); 

// definition. 
void f (int i) 
{ 
    std::cout << i << std::endl; 
    // ... 
} 

Чтобы добавить к путанице, некоторые заявления делать и в то же время. Например, если вы не объявляете функцию, определение также действует как объявление (это невозможно для статических переменных, как в случае с Something::s_nValue, который вы опубликовали).

1

Это аналогично случаю в C, где в файле заголовка вы могли бы сделать:

extern int Something_s_nValue; 

и вы исходный файл, который вы должны сделать следующее:

int Something_s_nValue; 

Первая часть является объявление, которое входит в ваш заголовочный файл, а вторая часть - это определение, которое входит в ваш исходный файл.

2

Объявление чего-то не совпадает с определением. Иногда вы можете делать оба одновременно, но в любом случае вам нужно как объявить, так и определить что-то.

Почему?

Хорошо, потому что стандарт говорит так, но почему стандарт так говорит?

Это связано с тем, как работают компиляция и компоновка. Если у меня есть несколько исходных файлов, a.cpp и b.cpp и несколько файлов заголовков, a.h и b.h, тогда я хочу их скомпилировать. Как правило, вы скомпилируете все исходные файлы по отдельности, чтобы получить a.o и b.o, а затем соединить их вместе в конце, чтобы получить окончательную программу.

Скажем, у нас было:

// a.h ========================= 
class A { static int n; }; 

// b.h ========================= 
class B { static int n; }; 

// a.cpp ======================= 
#include "a.h" 
#include "b.h" 

int foo() { return A::n + B::n; } 

// b.cpp ======================= 
#include "a.h" 
#include "b.h" 

int bar() { return A::n - B::n; } 

Помните, что #include по существу только вставляет другой файл внутри, включая файл. Так что все компилятор видит, когда мы собираем a.cpp и b.cpp является:

// a.cpp ======================= 
class A { static int n; }; 
class B { static int n; }; 

int foo() { return A::n + B::n; } 

// b.cpp ======================= 
class A { static int n; }; 
class B { static int n; }; 

int bar() { return A::n - B::n; } 

Какой файл объекта должен A::n и B::n идти? a.o или b.o? Он объявлен как в a.cpp, так и в b.cpp, поэтому компилятор понятия не имеет, куда его поместить. Если вы поместите его в оба, вы определите его дважды, и компилятор не будет знать, что использовать (в этом случае компоновщик даст вам ошибку с множественным определением символа).

Вот почему нам нужно определение. Определение говорит нам, какой объект файла поместить его в.

// a.cpp ======================= 
#include "a.h" 
#include "b.h" 

int A::n = 0; // A::n goes in a.o 

int foo() { return A::n + B::n; } 

// b.cpp ======================= 
#include "a.h" 
#include "b.h" 

int B::n = 0; // B::n goes in b.o 

int bar() { return A::n - B::n; } 

Стоит отметить, что вы могли бы положить как в a.cpp или как в b.cpp. Это не имеет значения, если он определен точно один раз.

Смежные вопросы