C++ 宣告與定義

Andy Niu發表於2013-10-13

一步一步講起:

1、C++程式生成可執行檔案的過程:原始檔(.cpp),經過預編譯,生成編譯單元;編譯單元,經過編譯,生成目標檔案(.obj);目標檔案,經過連線,生成可執行檔案。

2、預編譯做的事情:對#include,#define進行文字替換。

3、C++支援單獨編譯,也就是對每個編譯單元進行單獨編譯。這就要求,編譯單元是一個自包含的檔案,也就是說,編譯單元生成目標檔案的時候,不需要其他的資訊。因此,在編譯單元內部,所有使用的東西,必須要有定義,或者有宣告。定義的意思是:這個東西就放在這裡,當然可以使用。宣告的意思是:我有這個東西,但是這個東西放在其他地方,這樣的話,就可以使用宣告的東西了。

4、可以多次宣告,但只能一次定義(有例外)。為什麼?

我可以重複說,我有這個東西,我有這個東西。但是我不能把一個東西放在多個地方。

5、宣告只是說,我有這個東西,什麼時候定位到這個東西呢?

編譯的時候,宣告我有這個東西,就可以用了。在後面連線的時候,會從其他的目標檔案,找到這個東西。

6、可以多次宣告,但只能一次定義,是有例外的。有些情況不能重複宣告,那就是類的成員方法和靜態資料成員。

7、可以多次宣告,但只能一次定義,是有例外的。有些情況是允許重複定義的。為什麼允許重複定義?

在編譯單元中,在編譯的時候,對於有些東西,可以說,我有這個東西,就可以使用,等到連線的時候,找到這個東西。但是對於有些東西,只有宣告,說我有這個東西,是不行的,必須定義,才能使用。這樣就出現了,在多個編譯單元進行了重複定義。這樣的情況有:

  a、加 static 字首的全域性變數定義.如: static int x;

  b、列舉型別的定義.如: enum Boolean {NO,YES };

  c、類的定義. 如: class Point { int d_x; int d_y; ... };

  d、行內函數的定義.如: inline int operator==(const Point& left,const Point&right) { ... }

  e、union的定義.

  f、名字空間中const常量定義

為什麼只有宣告是不夠的呢?

這是因為編譯的時候,只有這些資訊是不夠的,比如類,只有宣告,是沒法分配記憶體的,內聯方法,只有宣告,是沒法展開進行文字替換的。

8、那麼問題來了,對於上面的情況,在多個編譯單元,都進行了定義,連線的時候,為什麼沒有重定義的錯誤呢?

這是因為,對於上面的情況,連線的時候,是內部連結的,也就是說,連線的時候,不到外面找相同的東西了。

9、上面說的是,在多個編譯單元之間,進行了重複定義,連線時不會報錯,是因為內部連線。那麼,在一個編譯單元之內,進行了重複定義呢?會出現什麼情況?

答案是:編譯錯誤。

10、在一個編譯單元內,不能進行重複定義。那麼,問題又來了,在預處理的時候,#include遞迴地進行文字替換,這種時候,就會出現重複定義,如何解決呢?

答案是:使用標頭檔案保護符。

#ifndef  XXX

  #define XXX

#endif

這樣,就避免了重複地替換,也就不會出現重複定義了。注意,標頭檔案保護符只是保證在一個編譯單元,不會重複定義。在多個編譯單元,還可能會重複定義。

11、也就是說,在一個編譯單元內,肯定不能重複定義,否則編譯錯誤。在多個編譯單元之間呢,對於內部連線的情況,允許重定義。對於外部連線的情況,不允許重複定義。

12、在多個編譯單元之間,為了避免出現重複定義,有兩種解決辦法:

  a、使用內部連線。

  b、使用外部連線,並且使用不具名空間。使用不具名空間,也是外部連線,為什麼連線的時候,不會報錯呢?

  這是因為,不具名空間,雖然是外部連線,但是可以認為不具名空間是個封裝的東西,外部連線想連線也連線不到。推薦使用外部連線。

相關文章