2011-09-25 56 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++的本質,當你定義一些你必須指定的確切類型時,即使你之前有過一個聲明。所有變量和函數都是如此。

旁註:你不重新定義它,這會導致編譯錯誤。你只需定義它。

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.cppb.cpp和幾個頭文件,a.hb.h然後我想編譯它們。一般來說,您可以單獨編譯所有源文件以獲得a.ob.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.cppb.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::nB::n進去? a.ob.o?它在a.cppb.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; } 

值得指出的是,你可以在b.cpp已經把無論是在a.cpp或兩者兼而有之。沒關係,只要定義一次。