首先,,我們來了解下定義: 內部連接:如果一個名稱對編譯單元(.cpp)來說是局部的,,在鏈接的時候其他的編譯單元無法鏈接到它,。 外部連接:如果一個名稱對編譯單元來說不是局部的,,而在鏈接的時候其他的編譯單元可以訪問它,,也就是說它可以和別的編譯單元交互,。 通過對LIB與DLL的講解,我們可以更方便的理解內部連接與外部連接,。 我們了解了一個編譯單元(.cpp)編譯成obj文件后,,至少還會有未解決符號表、導出符號表,、地址重定向表,。而如果這個名稱是內部連接的話,,那在導出符號表中不存儲它的入口。也就是別的obj文件無法鏈接到這個名稱,。而外部連接剛好相反,,在導出的符號表中有它入口。 以下情況有內部連接: a)所有的聲明 b)名字空間(包括全局名字空間)中的靜態(tài)自由函數,、靜態(tài)友元函數,、靜態(tài)變量的定義 c)enum定義 d)inline函數定義(包括自由函數和非自由函數) e)類的定義 f)名字空間中const常量定義 g)union的定義 以下情況是外部連接: a)非static全局變量與全局函數 b)類非inline函數總有外部連接。包括類成員函數和類靜態(tài)成員函數 c)類靜態(tài)成員變量總有外部連接 d)名字空間(包括全局名字空間)中非靜態(tài)自由函數,、非靜態(tài)友元函數及非靜態(tài)變量 好了,,我們通過程序來深刻的理解吧: 假設有3文件: TestBase.h: TestRun.cpp TestError.cpp int a; #include "TestBase.h" #include "TestBase.h" 當然還有一個包含main()方法的Test.cpp輸出的文件。 #include "TestBase.h" extern int a; void print() { cout << a<<endl; } 分別編譯TestRun與TestError我相信大家都能通過編譯,,但是你鏈接的時候肯定會出錯的,,提示的信息有一句為:Debug/Test.exe : fatal error LNK1169: one or more multiply defined symbols found(一個或多個定義符號被發(fā)現)。因為非static的全局變量是外部連接的,,其實也就是說TestRun.obj與TestError .obj的導出符號表中都對a導出了信息入口(別問我為什么導出了它,,因為編譯器默認對非static全局變量都導出了,想知道怎么實現的,,可以去找Microsoft),。而當我的Test.cpp中要用到a時我到底是用TestRun.obj還是TestError .obj中導出符號表中的a呢?所以鏈接肯定會出錯的,。 我們在嘗試著把TestBase.h中的全局變量a改了static,。馬上能編譯與鏈接成功,并輸出0,,其實是因為static全部變量是內部連接的,,obj文件的導出符號表中沒有提供a符號的入口。而Test.cpp用到的是自己在編譯的時候得到的a的信息,,也就是TestBase.h文件中a的默認值0. 函數的性質也是一樣的,,所以大家只需要知道你聲明或定義的名稱是內部連接還是外部連接,如TestBase.h中的非全局變量a是外部連接,。你就會明白語法其實也就那么回事了。 讓我們在來了解class里的static變量和非static函數是什么樣子的,。 class是內部連接的,,這就是為什么可以有多個cpp文件能包含它的原因了,但是如果我在class里寫了個static變量了,,那這個變量就是外部連接了,。而內中的非static函數了是外部連接。所以針對class我總結了下3點必須: (1),。類中的static變量請不要在聲明類中定義,。一般我們類的聲明是寫到.h文件中,,而實現寫到對應的cpp文件中的,理由看程序,。 還是剛才3個文件: TestBase.h: TestRun.cpp TestError.cpp class A #include "TestBase.h" #include "TestBase.h" { int A::getA() public: { void setA( int a ); return m_a; int getA(); } inline int getB(); void A::setA( int a ) private: { int m_a; m_a = a; static int m_b; } }; int A::m_b = 5; 編譯都成功后,,鏈接會出現如下信息: TestError.obj : error LNK2005: "private: static int A::m_b" (?m_b@A@@0HA) already defined in Test.obj TestRun.obj : error LNK2005: "private: static int A::m_b" (?m_b@A@@0HA) already defined in Test.obj Debug/Test.exe : fatal error LNK1169: one or more multiply defined symbols found 也就是說類中的static變量m_b為外部連接,理由同上面寫的全局變量a一樣,。解決辦法是把int A::m_b = 5;寫到類的實現文件TestRun.cpp 中,,這樣就只有TestRun.obj文件的導出符號表中提供了m_b,而其他的obj文件如果需要的話,,那這只能在它的未解決符號表中存在了,,它自然只能在TestRun.obj文件中找到m_b的入口,這樣就無任何沖突了,。所以類中的static變量請一定不要在聲明類中定義,。 (2)。內中的非static函數請一定也不要在聲明類實現,,除非你的聲明和實現是寫到一起的,。 TestBase.h: TestRun.cpp TestError.cpp class A #include "TestBase.h" #include "TestBase.h" { int A::getA() public: { void setA( int a ); return m_a; int getA(); } inline int getB(); int A::m_b = 6; private: int m_a; static int m_b; }; void A::setA( int a ) { m_a = a; } 編譯通過,鏈接后肯定不能通過,,原因都一樣,,就是因為類中的非static函數是外部鏈接。而如果你把定義和聲明寫到一起,,那就沒問題,,但你應該知道這樣寫就是相當于出賣了自己,你把你的實現代碼都給了別人,。 (3)inline函數請一定要在類的聲明文件中實現,。大家應該看到類中的inline函數我沒有寫它的實現代碼,其實也就是為了這條定義一樣,。你可以查看VC提供的頭文件中inline的定義與實現都是寫在頭文件中的,。 所以那個inline函數你必須寫在TestBase.h內。 inline int A::getB() { return m_b; } 為什么這樣呢,?因為inline函數是內部連接,,它不在導出符號表。假如你把inline int getB()的實現代碼寫在了 TestRun.cpp中,,那鏈接后的錯誤信息是: Debug/Test.exe : fatal error LNK1120: 1 unresolved externals 也就是別的cpp文件就無法用到找到它了,。 好了,其實很多東西如果是在鏈接時候出錯了,,或者為什么語法這么實現的,,你可以從它是內部連接還是外部鏈接入手,你會發(fā)現很多規(guī)律以及規(guī)則的存在,這樣你的編寫的C++的代碼安全性一定會大大的提高,。相信我,! |
|
來自: 昵稱16235376 > 《C 》