轉自:http://blog.csdn.net/hazir/article/details/38600419
今天下午遇到一個頭文件相互包含而導致的編譯問題,花了我不少時間去調試沒找到問題,最后晚上跟師兄討論不少時間,突然有所頓悟!
問題重現
我把問題脫離於項目簡單描述一下:我寫了一個函數 bool func(ClassA* CA)
需要加到項目中,我就把這個函數的聲明放到 head1.h
中,函數參數類型 ClassA 定義在另一個頭文件 head2.h
中,因此我需要在 head1.h
中包含 head2.h
;而 head2.h
中之前又包含了 head1.h
,這樣就構成了一種頭文件相互包含的場景。再加上一些其它的聲明與定義,就構成了這樣的一個文件結構:
- head1.h
#ifndef __HEAD_1_H__
#define __HEAD_1_H__ #include "head2.h" #define VAR_MACRO 1 //define a macro, which used in head2.h bool func(ClassA* CA); //ClassA is defined in head2.h #endif
- head2.h
#ifndef __HEAD_2_H__
#define __HEAD_2_H__ #include "head1.h" class ClassA{ int mVar; void setMem(){ mVar = VAR_MACRO }; //macro VAR_MACRO is defined in head1.h ... //other members and functions }; #endif
那么,現在另有兩個源文件
- source1.cpp
#include "head1.h"
//... some source code
- source2.cpp
#include "head2.h"
//... some source code
整個項目會分別編譯這兩個源文件,編譯完之后會報錯,大致意思是 ClassA 和 VAR_MACRO 沒有定義,那么問題就比較奇怪了,每個頭文件都分別引用了另一個頭文件,為什么會出現未定義呢?
問題分析
我們都知道 C/C++ 中頭文件開始習慣使用 #ifndef ... #define ... #endif
這樣的一組預處理標識符來防止重復包含,例如上面的問題中我也使用了,如果不使用的話,兩個頭文件相互包含,就出現遞歸包含。這個很好理解就不多敘。
回到問題本身,我在微博上貼出了這個問題,有人說在 head1.h
的函數前加上 ClassA A;
的前置聲明,至於為什么要加,估計很多人都沒理解...
對於 source1.cpp
,它包含了頭文件 head1.h
,那么在編譯之前,在 source1.cpp
中展開 head1.h
,而 head1.h
又包含了 head2.h
, 那么也展開它,這時 source1.cpp
就變成類似下面這樣:
class ClassA{ int mVar; void setMem(){ mVar = VAR_MACRO }; //macro VAR_MACRO is defined in head1.h ... //other members and functions }; #define VAR_MACRO 1 //define a macro, which used in head2.h bool func(ClassA* CA); //ClassA is defined in head2.h //... source1.cpp source code
看到沒,這地方 func 函數之前有 ClassA 類型的定義,根本沒必要像有些人說的那樣加上 ClassA CA;
這樣的前置聲明。
我們再展開 source2.cpp
看看:
#define VAR_MACRO 1 //define a macro, which used in head2.h bool func(ClassA* CA); //ClassA is defined in head2.h class ClassA{ int mVar; void setMem(){ mVar = VAR_MACRO }; //macro VAR_MACRO is defined in head1.h ... //other members and functions }; //... source2.cpp source code
這時問題就很清楚了,func 函數聲明之前並沒有發現 ClassA 類型定義,該定義在函數聲明的后面,這時候如果能在head1.h
的函數聲明之前加上 ClassA CA;
的前置聲明,就不會在編譯的時候報找不到 ClassA 的定義的錯誤了。
再回到 source1.cpp
展開的源碼看看,是不是一下子明白了為什么報找不到 VAR_MACRO 的定義的錯誤了?修改方法也簡單,把宏定義拉到 #include "head2.h"
語句之前就 OK 了。
問題反思
現在回頭想想這個問題,其實是個非常簡單的頭文件包含的問題,如果了解一些編譯器的預編譯過程,錯誤原理也很簡單。但為什么我卡在這個問題很長時間,原因有以下幾點:
- 問題出現在一個項目的編譯過程中,未能准確地定位問題的原因
- 出現問題,沒有靜下來心來理清問題,C/C++ 編譯基礎知識點雖然知道,但未能第一時間運用到該問題的分析上
- 師兄說加上前置類的聲明,確實解決了類類型找不到的錯誤,雖然有問題解決方法,但沒有真正理解這種做法的道理,以至於宏定義找不到的錯誤還是不知如何去解決(其實本質是一樣的)
總的來說,遇到問題不要慌,保持大腦清醒,把加一行或減一行代碼期望就能碰運氣編譯通過的時間拿來分析問題更有效,解決問題之后一定確定自己是否知其然亦知其所以然!
參考資料
Google找了一圈,沒找到有價值的資料....