2012-07-13 37 views
5

我認爲我對命名空間和/或靜態變量存在根本的誤解。但我曾嘗試這個測試代碼(用手打字,原諒錯別字)如何在命名空間中使用變量

test.h:

namespace test{ 
    static int testNum=5; 
    void setNum(int value); 
} 

main.cpp中:

#include <test.h> 

int test::setNum(int value){ 
    testNum=value; 
} 

int main(){ 
    test::setNum(9); 
    cout<<test::testNum; 
} 

當我運行此我得到的值5 ,而不是我所預期的那樣。看起來好像我有兩個testNum變量的實例,但這似乎與靜態應該做的事情完全相反。我猜我犯了一個錯誤,認爲這些功能與他們的java equvilants完全相同......

我還得到一個錯誤,指出testNum聲明多次,如果我從我的聲明中刪除靜態testNum,有人可以解釋爲什麼這種情況呢?

謝謝

+1

您知道'#include'的工作原理嗎?它複製粘貼指定文件的內容。 ''main.cpp'中的'setNum'定義是否也是如此? – Xeo 2012-07-13 20:53:01

+0

我沒有顯示它,但是我在.h中有標準的ifNDEF DEF。此外,由於我只在整個程序中包含它*一次,它仍然不是一個問題嗎? – dsollen 2012-07-13 20:53:48

+0

你使用'使用命名空間測試'嗎? – 2012-07-13 20:54:13

回答

14

首先,你的誤解有沒有什麼與命名空間有關,它只是關於static。對於這個答案的其餘部分,我將簡單地提及testNum,因爲它在命名空間中的事實是無關緊要的。

我還假設你有另一個文件,可能被稱爲test.cpp,其中還包括test.h並定義了setNum函數。

當聲明名稱空間範圍(即不是類成員或函數本地)的變量或函數時,它表示實體的名稱是該文件的內部名稱。形式上它具有「內部連接」,這意味着它不能由名稱被稱爲或從其它文件鏈接到(它可以通過指針或通過將其作爲參數傳遞給另一個函數來間接參照。)這意味着如果幾個文件定義static int testNum然後每個文件都有自己的內部變量與該名稱,不同於其他文件中的testNum(實際上一個文件可能有static int testnum,另一個文件可能有static double testnum和另一個static char* testNum,它們都是不同的和內部的每個文件)。如果你把一個定義一樣,在頭然後每一個包括頭都有自己的testNum文件。

因此,與static在頭的變量,你有不同變量在每一個包括test.h文件名爲testNum。這意味着如果你在一個文件中設置testNum,並呼籲在使用testNum它指的是一個不同的變量,這恰好具有相同名稱的不同文件中的函數。

正因爲如此,聲明非const在頭static變量幾乎總是錯的。

如果沒有static,您應該在每個文件中定義一個testNum變量,其中包括test.h,這是不允許的:每個實體只能在您的程序中一次性定義。解決這個問題的方法是聲明在頭部的變量,而不是定義它,你做的,告訴編譯器變量是extern

extern int testNum; // N.B. no "= 1" here 

告訴編譯器有一個變量與「外部鏈接」叫testNum,所以當代碼是指testNum它總是意味着相同的變量(不是一些名字與內部linakge是在每個文件不同的實體。)聲明一個extern變量後,它是你的責任,以確保有恰恰是在某處程序提供一個定義,所以在只有一個檔案(即而不是包含在多個文件中的標題),您可以定義它:

int testNum = 1; 
+0

謝謝,這解釋了我的問題。正如我以爲我正在對我的Java開發中的靜態工作做出錯誤的假設,儘管我首先查了一下靜態,並認爲C++靜態也是一樣的......在你看到你期待的答案的情況下,我想是這樣。我真的很喜歡我的真正的程序,無論是什麼(即使有人去改變我的main.h),都要將靜態值默認爲安全的,以確保我沒有奇怪的功能。看起來好像extern阻止我讓那個gaurentee從一個有效的默認值開始。任何方式來確保? – dsollen 2012-07-13 21:17:24

+0

'static'意味着C++中的幾件事情。對於_class members_,它與Java相似,但是在程序中沒有類成員,您有非成員全局變量,而這些全局變量在Java中不存在。 – 2012-07-13 21:20:56

+0

我不知道你的意思是關於extern防止「默認」值或「默認安全」。我在答案中給出的「= 1」值有什麼問題? – 2012-07-13 21:22:19

3

static在命名空間內是用詞不當,不應該被使用。它 意味着聲明靜態的實體具有內部名稱綁定; 換句話說,在其他翻譯單位中的相同名稱將 指向另一個實體,並且在變量定義的情況下,該 將在每個翻譯單位中存在單獨的變量實例。它對生命沒有影響。 (所有變量聲明或在命名空間內定義 有靜態一生。)

static在命名空間內也已過時。不要使用它。

關於在標題中聲明變量:以 extern而不是static作爲其前綴。如果變量聲明爲extern和 沒有初始化,則該聲明不是一個定義。 當然,在這種情況下,您必須在某處提供定義(在 單個源文件中)。

extern int testNum = 5; 
int testNum = 5; 
int testNum;   // implicitly initialized with 0. 

編輯:

要有所澄清:有一些混亂這裏的壽命和 名綁定之間:沿線的東西

  • 的對象有一個生命週期(自動,靜態或動態—或臨時或異常)和
  • 名稱被綁定到實體;如果名稱被聲明爲變量,則該實體是一個對象。

混淆靜態一生關鍵字static。 (功能 可以static,但功能必須在C++中沒有定義的壽命;他們 就在那裏。)

關於這些規則並不是很orthognal。基本上, 關於一生:

  • 在命名空間內聲明的所有變量有靜態一生,總是
  • 變量在局部範圍內聲明的具有自動一生除非他們被聲明static,並
  • 變量在類範圍聲明的類對象的生命週期包含它們,除非它們被聲明爲static。 關於生命。

對象與靜態終身進入main之前是一段,而 住,直到你從main返回後。

至於名稱綁定:在命名空間內聲明的

  • 變量有外部名綁定, 除非將它們聲明static,在這種情況下,他們有內部 名綁定(但使用static已過時),或者如果他們 const,而不是宣佈extern,在班級範圍內聲明
  • 變量有外部名綁定,即使它們被聲明static,並
  • 在塊範圍聲明的變量沒有約束力。

最後,還有一個問題,即聲明是否是定義 或不是。如果是定義,則分配內存,並且對象是 (或可能)被初始化。如果它不是一個定義,它只是告訴 編譯器在聲明中聲明的實體 (object)有一個定義。一般來說,變量 聲明是一個定義,除非它被聲明爲extern並且 不是有一個初始化程序。

+0

謝謝你的解決方案(我會在一秒鐘內去測試),但對於我自己的啓發,你能更好地解釋問題是什麼?你說名稱空間中的所有變量都是靜態的,但這仍然讓我想知道爲什麼在這個例子中我的代碼不能工作,因爲我需要的只是一個靜態變量。你留下來,我結束了每個地方我引用testnum聲明兩個具有相同名稱的靜態變量? – dsollen 2012-07-13 21:05:10

+0

@dsollen我已編輯添加一些說明。 – 2012-07-13 21:23:45

1

你可能想確保你將它張貼問出了什麼問題之前,你的代碼實際上有問題;)

我複製/粘貼於你的錯別字,並手動做的包括:

#include <iostream> 
using namespace std; 

namespace test{ 
    static int testNum=5; 
    void setNum(int value); 
} 

void test::setNum(int value){ 
    testNum=value; 
} 

int main(){ 
    test::setNum(9); 
    cout<<test::testNum; 
} 

結果:

$ ./a.out 
9 

你沒有說的是你的程序還有什麼。如果你不僅僅是main.cpp,而且還包含你的test.h,那麼每個.cpp文件都會有它自己的testNum副本。如果你想讓他們分享,那麼你需要除了一個之外的所有東西來標記它爲extern