| 版權聲明:本文為博主原創文章,未經博主允許不得轉載。
關於頭文件重復包含的問題,以前一直不太清楚,今天特意翻了一下參考書和網上查閱資料,有了如下的理解:
這樣說明,如果定義了頭文件A.h,B.h和源文件C.cpp。如果我們在A.h中寫上一個函數,在B.h中include A.h,然后再在C.cpp中include A.h和B.h,這樣我們就會出現重復包含的問題,如下圖:
下面看下問題代碼:

1 //////////////////////////////////////////////////////////////////////////////////////////////////// A.h files 2 3 //用於測試頭文件的重復包含和重定義的問題 4 #include <iostream> 5 using namespace std; 6 7 int sub(int a, int b) 8 { 9 std::cout << "sub: a-b = " << a - b << std::endl; 10 return a - b; 11 } 12 13 int add(int a, int b) 14 { 15 std::cout << "sub: a+b = " << a + b << std::endl; 16 17 return a + b; 18 } 19 20 21 //////////////////////////////////////////////////////////////////////////////////////////////////// B.h files 22 23 #include <iostream> 24 using namespace std; 25 26 #include "A.h" 27 28 int mul(int a, int b) 29 { 30 std::cout << "sub: a*b = " << a * b << std::endl; 31 32 return a * b; 33 } 34 35 36 37 //////////////////////////////////////////////////////////////////////////////////////////////// C.cpp files 38 39 #include <iostream> 40 using namespace std; 41 42 #include "A.h" 43 #include "B.h" 44 45 int main(void) 46 { 47 48 //add(1, 2); 49 //sub(45, 45); 50 51 return 0; 52 }
出錯截圖:
*.出錯分析:
我們可以看到錯誤代碼的提示:error C2084: function 'int sub(int,int)' already has a body,這說明sub函數已經存在了一個。這是什么原因,因為在B.h中我們通過#include "A.h"導入了A.h的頭文件。而我們都知道頭文件處理簡單的來說就是將A.h中文件中的所有的東西全部的復制到#include <A.h>的定義所在的文件處。既然如此我們就可這樣的理解,如下代碼:

1 //////////////////////////////////////////////////////////////////////////////////////////////////// A.h files 2 3 //用於測試頭文件的重復包含和重定義的問題 4 #include <iostream> 5 using namespace std; 6 7 int sub(int a, int b) 8 { 9 std::cout << "sub: a-b = " << a - b << std::endl; 10 return a - b; 11 } 12 13 int add(int a, int b) 14 { 15 std::cout << "sub: a+b = " << a + b << std::endl; 16 17 return a + b; 18 } 19 20 21 //////////////////////////////////////////////////////////////////////////////////////////////////// B.h files 22 23 #include <iostream> 24 using namespace std; 25 26 //#include "A.h" 27 //#include "A.h"就是相當於將A.h中的內容copy到了B.h中 28 29 ///////////////////////////////////////////////////////////////////////////////////////////////////// 30 //copy部分A.h中的函數,如下: 31 int sub(int a, int b) 32 { 33 std::cout << "sub: a-b = " << a - b << std::endl; 34 return a - b; 35 } 36 37 int add(int a, int b) 38 { 39 std::cout << "sub: a+b = " << a + b << std::endl; 40 41 return a + b; 42 } 43 //////////////////////////////////////////////////////////////////////////////// 44 45 int mul(int a, int b) 46 { 47 std::cout << "sub: a*b = " << a * b << std::endl; 48 49 return a * b; 50 } 51 52 53 54 //////////////////////////////////////////////////////////////////////////////////////////////// C.cpp files 55 56 #include <iostream> 57 using namespace std; 58 59 #include "A.h" 60 #include "B.h" 61 62 //這里的A.h和B.h中的內容也會copy到C.cpp中來 63 64 int main(void) 65 { 66 67 //add(1, 2); 68 //sub(45, 45); 69 70 return 0; 71 }
那么,這樣看之后,我們就可以發現一個問題,在A.h中有兩個函數sub()和add()定義;在B.h中同樣的包含了兩個函數sub()和add()定義,當A.h和B.h單獨分開始這樣還不會有問題。但是我們在看下C.cpp文件中有:#include "A.h" 和 #include "B.h"。這樣的話又重復的將A.h和B.h文件中的內容復制到了C.cpp中,那么這樣就出現問題了,在A.h和B.h中的相同函數全部復制到C.cpp中。那么在編譯的時候編譯器就會發現重定義了兩個相同的函數,也就是上面的錯誤。編譯上面Multiple define error: 2中的代碼同樣出現於Multiple define error: 1中的一樣的錯誤。
因此,總的來說,頭文件重復包含的問題往往是重定義的問題。下面我們有兩種方式解決頭文件的重復包含:一個是條件編譯的#ifndef...#endif 和 #pragma once.
如下代碼示例:
////////// C.cpp files #include <iostream> using namespace std; #include "A.h" #include "B.h" int main(void) { add(1, 2); sub(45, 45); return 0; } /////////// A.h files //用於測試頭文件的重復包含和重定義的問題 #ifndef _A_H_ #define _A_H_ #include <iostream> using namespace std; int sub(int a, int b) { std::cout << "sub: a-b = " << a - b << std::endl; return a - b; } int add(int a, int b) { std::cout << "sub: a+b = " << a + b << std::endl; return a + b; } #endif // _A_H_ ///////////// B.h files #ifndef _B_H_ #define _B_H_ #include <iostream> using namespace std; #include "A.h" int mul(int a, int b) { std::cout << "sub: a*b = " << a * b << std::endl; return a * b; } #endif // _B_H_
*.如上代碼加入#ifndef...#endif就消除了重定義的問題了。其實這種方法消除的原理是在當編譯器遇到第2(3,....)遍同樣的頭文件時,因為已經編譯了一次,在后面在遇到的時候,編譯器自動忽略文件中的內容。
/////////// C.cpp #include <iostream> using namespace std; #include "A.h" #include "B.h" int main(void) { add(1, 2); sub(45, 45); return 0; } //////////////// A.h //用於測試頭文件的重復包含和重定義的問題 //#ifndef _A_H_ //#define _A_H_ #pragma once #include <iostream> using namespace std; int sub(int a, int b) { std::cout << "sub: a-b = " << a - b << std::endl; return a - b; } int add(int a, int b) { std::cout << "sub: a+b = " << a + b << std::endl; return a + b; } //#endif // _A_H_ ///////////////////// B.h //#ifndef _B_H_ //#define _B_H_ #pragma once #include <iostream> using namespace std; #include "A.h" int mul(int a, int b) { std::cout << "sub: a*b = " << a * b << std::endl; return a * b; } //#endif // _B_H_
*.#pragma once同樣能消除頭文件的重復包含的問題。見下運行實例圖:
同樣的上面的#ifndef...#endif 和 #pragma once 並不能完全消除所有的重定義問題。如下代碼:

1 ////////////////////// A.h 2 3 #ifndef _A_H_ 4 #define _A_H_ 5 6 int a = 10; 7 8 #include <iostream> 9 using namespace std; 10 11 int sub(int a, int b) 12 { 13 std::cout << "sub: a-b = " << a - b << std::endl; 14 return a - b; 15 } 16 17 int add(int a, int b) 18 { 19 std::cout << "sub: a+b = " << a + b << std::endl; 20 21 return a + b; 22 } 23 24 #endif // _A_H_ 25 26 27 //////////////////// B.h 28 29 #ifndef _B_H_ 30 #define _B_H_ 31 32 #include <iostream> 33 using namespace std; 34 //#include "A.h" 35 36 int a = 5; 37 38 int mul(int a, int b) 39 { 40 std::cout << "sub: a*b = " << a * b << std::endl; 41 42 return a * b; 43 } 44 45 #endif // _B_H_
Multiple define error: 3出現如下錯誤:
看上面的代碼,我們已將a變量的聲明放在了條件編譯之中,但是還是出現了重定義的問題,這是什么原因呢?在C++靜態變量中有三種類型,其中有一種外部鏈接型的靜態變量。而全局變量在聲明的時候沒有加上修飾詞static,因此這種變量是屬於外部鏈接型的變量。而外部鏈接的變量對於在同一個工程文件的所有文件中都是可見的,所以在其中一個文件中定義的外部鏈接變量a,同時在另一個文件中也定義。或者在包含頭文件的時候重復包含了某一個定義了外部變量的頭文件,就會導致在同一個工程文件形成的程序中將包含兩個一樣的變量(同樣函數定義(Multiple define error:2)也是一樣會產生同樣的錯誤)。
如何解決呢?解決的方法就是在a變量前加上extern修飾符即可;如下:
extern int a = 10;
因此,在頭文件的定義中,我們最好不要將函數定義和變量的聲明放到頭文件中。但是下面的幾種確實頭文件常包含的內容:
函數聲明 使用#define或者const定義的符號常量 類聲明 結構體聲明 模板聲明 內聯函數
如下:
#ifndef _B_H_ #define _B_H_ #include <iostream> using namespace std; #include "A.h" #define pi 3.1415 const int value = 100; int add(int a, int b); class bb { //...... }; struct bbb { //...... }; inline void bbbb(int a, int b) { cout << "a+b = " << a+b << endl; } int mul(int a, int b); //{ // std::cout << "sub: a*b = " << a * b << std::endl; // // return a * b; //} #endif // _B_H_
其中函數聲明不用說都知道,類聲明和結構體聲明在頭文件中不會導致重定義是因為類和結構體中的不會創建變量只是在源文件中聲明結構體變量的時候。因此不會出現重定義問題。內聯函數和模板的聲明這兩個我還沒有查到具體的原因(待續)。
-----------如有錯誤,希望大家多多指正-----------