2013-04-11 184 views
0

我最近遇到了一個問題。C++鏈接錯誤,符號重定義

我有三個文件啊,B.cpp,C.cpp:

#ifndef __A_H__ 
#define __A_H__ 

int M() 
{ 
    return 1; 
} 

#endif // __A_H__ 

B.cpp

#include "A.h" 

C.cpp

#include "A.h" 

由於我通過MSVC三個文件comile,有一個錯誤:

C.obj : error LNK2005: "int __cdecl M(void)" ([email protected]@YAHXZ) already defined in B.obj 

這是很容易理解,因爲我們知道,B.OBJ有一個名爲 「M」,也C.obj有一個 「M」 的標誌。 這裏出現錯誤。

然而,如果我改變M法,其含有一個方法M.這樣下面的類:

A.H

#ifndef __A_H__ 
#define __A_H__ 

class CA 
{ 
public: 
    int M() 
    { 
     return 1; 
    } 
}; 

#endif // __A_H__ 

沒有更多的錯誤!有人能告訴我發生了什麼事嗎?

+1

您不能在頭文件中定義函數,而是在頭文件中聲明它們。但是,類可以在頭文件中聲明。所有函數定義都應該轉到源文件,除了少數內聯。 – 2013-04-11 06:24:12

回答

1

如果B.cpp和C.cpp包括A.H,那麼兩者都編譯的M定義,所以這兩個目標文件將包含代碼M。當鏈接器收集所有功能時,他發現M在兩個對象文件中定義,並且不知道要使用哪一個。因此鏈接器提出了一個LNK2005。

如果您將函數M放入類聲明,那麼編譯器會將內聯函數標記/處理爲M。這些信息被寫入目標文件。鏈接器看到這兩個目標文件包含一個在線版本的CA::M的定義,所以他假定兩者相等,並隨機挑選了兩個定義之一。

如果你寫了

class CA { 
public: 
    int M(); 
}; 

int CA::M() 
{ 
    return 1; 
} 

這會造成同樣的問題(LNK2005)爲您最初的版本,因爲那時CA::M不會有任何內嵌多。

因此,現在你可能會猜到,有兩種解決方案適合你。如果你想M被內聯,然後更改您的代碼

__inline int M() 
{ 
    return 1; 
} 

如果你不關心內聯,那麼請做標準的方式,把功能聲明到頭文件:

extern int M(); 

並把功能定義成CPP文件(啊,這將理想A.cpp):

int M() 
{ 
    return 1; 
} 

請注意,extern在頭文件中並不是必須的。

另一個用戶建議你寫

static int M() 
{ 
    return 1; 
} 

I'ld不推薦這樣。這意味着編譯器將M放入兩個對象文件中,並將M標記爲僅在每個對象文件本身中可見的函數。如果鏈接程序發現B.cpp中的函數調用M,則它會在B.obj和C.obj中找到M。兩者都將M標記爲靜態,因此鏈接程序忽略C.obj中的M並從B.obj中選取M。反之亦然,如果C.cpp中的函數調用M,則鏈接程序將從C.obj中選取M。您最終會得到多個M的定義,所有這些定義都是相同的。這是浪費空間。

+0

這是一個更詳細的答案,應該標記爲正確的答案,但是從我所知道的靜態成員應該只在內存中構建函數的一個副本,而不會浪費空間。我也覺得建議Quark使用更多的C++ Like方法解決這個問題。 – IssamTP 2013-04-11 07:59:52

+0

感謝您的回覆。這是非常詳細的和幫助。 – Quark 2013-04-11 10:59:35

0

我不知道引擎蓋下是什麼,但如果你不需要一類我想,編譯器會自動加上「外部」關鍵是你的功能,所以你會得到包括報頭中的錯誤2次。

您可以添加static關鍵字M()方法,所以你必須只有一個內存功能的副本,並在編譯時沒有錯誤。

順便說一句:我看你有一個#ENDIF,但不是的#ifdef或的#ifndef,這是一個複製/粘貼錯誤?

+0

對不起,這是複製/粘貼錯誤。 – Quark 2013-04-11 10:48:04