2010-01-20 32 views
9

我有一個頭部首警衛和LNK4006

//header.h 
const char* temp[] = {"JeffSter"}; 

的報頭中定義的字符陣列是否#define的防護,並在頂部具有的#pragma一次。如果這個頭文件包含在多個地方,我會得到一個已經在blahblah.obj中定義的LNK4006 - char const * * temp。所以,我有幾個關於這個問題

  1. 爲什麼這會發生,如果我有守衛?我認爲他們阻止了第一次訪問後頭部被讀入。
  2. 爲什麼在這個頭文件中的衆多枚舉不會給出LNK4006警告?
  3. 如果我在簽名之前添加靜態,我不會收到警告。這樣做的含義是什麼?
  4. 有沒有更好的方法來做到這一點,以避免錯誤,但讓我在頭中聲明數組。我真的很討厭有一個cpp文件只是爲了一個數組定義。

回答

12

爲什麼會發生這種情況,如果我有到位的後衛?我認爲他們阻止了第一次訪問後頭部被讀入。

包括警衛確保頭只包含一次一個文件(翻譯單元)。對於包含標題的多個文件,您希望將標題包含在每個文件中。

通過定義,而不是在頭文件中聲明變量具有外部鏈接(全局變量),你只能包括一次源文件中的標題。如果將標題包含在多個源文件中,則會有多個變量的定義,這在C++中是不允許的。因此,正如你已經發現的那樣,在頭文件中定義變量是一個壞主意,正是出於上述原因。

爲什麼在這個頭文件中的衆多枚舉不會也給LNK4006警告?

因爲它們沒有定義「全局變量」,它們只是關於類型的聲明等,它們不保留任何存儲空間。

如果我在簽名之前添加靜態,我不會收到警告。這樣做的含義是什麼?

當你做出一個變量static,它具有靜態範圍。該對象在定義它的翻譯單元(文件)之外不可見。所以,簡單來說,如果您有:

static int i; 

在你的頭,每一個源文件在其中包括頭將獲得單獨int變量i,這是源文件的看不見外面。這就是所謂的內部聯動

有沒有更好的方法來做到這一點,以避免錯誤,但讓我聲明數組頭。我真的很討厭有一個cpp文件只是爲了一個數組定義。

如果你想在陣列從所有你的C++文件中的一個對象可見,你應該做的:

extern int array[SIZE]; 

在你的頭文件,然後包括在所有的C++源文件的頭文件那需要變量array。在源(.cpp)文件中的一個,您需要定義array

int array[SIZE]; 

您應該包括上述源文件中的標題爲好,以允許在頭捕捉由於錯誤的區別和源文件。

基本上,extern告訴編譯器「array定義在某處,並具有類型int和大小SIZE」。然後,你實際上定義array只有一次。在鏈接階段,一切都很好地解決。

4

包括保護措施可以防止您將同一個標題反覆包含到同一個文件中 - 但不能將其包含在不同的文件中。
什麼情況是,鏈接器看到temp在一個以上的目標文件 - 你可以解決,通過使temp靜態或把它變成一個無名的命名空間:

static const char* temp1[] = {"JeffSter"}; 
// or 
namespace { 
    const char* temp2[] = {"JeffSter"}; 
} 

或者您可以使用它定義temp一個源文件而剛剛宣佈它爲extern在標題:

// temp.cpp: 
const char* temp[] = {"JeffSter"}; 

// header.h: 
extern const char* temp[]; 
-1

等一下...你是你的聲明混合起來......你是說「字符常量*臨時」但在你的頭文件,你有「常量char * temp [] = {「JeffSter」};'。

見節C FAQ 6.1,下「第6數組和指針」,引用:

 
6.1: I had the definition char a[6] in one source file, and in 
    another I declared extern char *a. Why didn't it work? 

A: In one source file you defined an array of characters and in the 
    other you declared a pointer to characters. The declaration 
    extern char *a simply does not match the actual definition. 
    The type pointer-to-type-T is not the same as array-of-type-T. 
    Use extern char a[]. 

    References: ISO Sec. 6.5.4.2; CT&P Sec. 3.3 pp. 33-4, Sec. 4.5 
    pp. 64-5. 

這是問題的根源。匹配你的聲明和定義。對不起,如果這聽起來很生硬,但我不禁會注意到鏈接器告訴你什麼...

希望這會有所幫助, 祝你好運, Tom。

+1

鏈接器只是將數組語法轉換爲指針語法。它不是一個錯誤,它只是一個警告,程序按預期執行。 – Steve 2010-01-21 00:11:44

4
  1. 頁眉衛士擁有絕對無關,與防止您的整個程序多個定義。標頭防護的目的是防止將相同的頭文件多次包含到同一個翻譯單元(.cpp文件)中的。換句話說,它們的存在是爲了防止中的多個定義在同一源文件中。他們在你的情況下按照預期工作。

  2. 控制C++中多重定義問題的規則稱爲一個定義規則(ODR)。對於不同類型的實體,ODR的定義是不同的。例如,類型允許在程序中具有多個相同的定義。他們可以(並且最經常地具有)在使用它們的每個翻譯單元中定義。這就是爲什麼你的枚舉定義不會導致錯誤。

    對象與外部聯動是完全不同的故事。他們必須在一個且僅有的一個翻譯單元中進行定義。這就是爲什麼當您將頭文件包含到多個翻譯單元時,temp的定義會導致錯誤。包括警衛無法防止此錯誤。只是不要在頭文件中定義具有外部鏈接的對象。

  3. 通過添加static你給你的對象內部聯動。這將使錯誤消失,因爲從ODR的角度來看,這完全可以。但是這將在您的頭文件包含在其中的每個翻譯單元中定義一個獨立的temp對象。爲了實現你也可以做

    const char* const temp[] = { "JeffSter" }; 
    

    因爲用C const ++對象在默認情況下內部鏈接的效果相同。

  4. 這取決於您是否需要具有外部鏈接的對象(即整個程序一個)或具有內部鏈接的對象(每個翻譯單元都是唯一的)。如果您需要後者,請使用static和/或額外的const(如果這對您有用),如上所示。

    如果你需要前者(外部鏈接),你應該把一個非定義聲明到頭文件

    extern const char* temp[]; 
    

    和移動定義放入一個且只有一個.cpp文件

    char* const temp[] = { "JeffSter" }; 
    

    頭文件中的上述聲明可用於大多數目的。但是,它將temp聲明爲未知大小的數組 - 一個不完整的類型。如果你想將其聲明爲已知大小的數組,你必須指定大小手動

    extern const char* temp[1]; 
    

    並記住保持在-同步的聲明和定義之間。

0

我尊敬地反對在頭文件中定義變量的建議,因爲我認爲「從不」太寬泛。儘管如此,這段話給我帶來了這樣的線索,爲那些敢於這樣做的人提供了一個警示性的故事。

作爲對LNK4006警告原因的一項調查的結果,我登陸了該頁面,調用了一個長期建立的數組,我剛剛從定義我的DLLMain例程的轉換單元移動到私有標題中,該標題包含在大多數構成這個圖書館的翻譯單位。在過去的11年裏,我已經編譯了這個庫數百次,而且我從未見過這個警告。

在我讀完這個頁面之後不久,我發現了錯誤的原因,這個錯誤的原因是這個定義在保護塊之外,它保護了模塊中定義的所有其他東西,這些定義也定義了DLLMain,這是我通常收集的地方所有需要外部鏈接的內存塊。正如預期的那樣,移動警戒區內的表格消除了警告,只留下了兩個,涉及到一個全新的外部鏈接表,待解決。

外賣:您可以在標題中定義變量,它是放置常見塊的好地方,但請注意警衛。