類的前置宣告(forward declaration)和包含標頭檔案(#include)的區別常常會迷惑我們,特別是涉及兩個類相互包含的時候。因此我們有必要搞清楚二者的區別以及二者的適用場合。
首先我們需要問乙個問題是:為什麼兩個類不能互相包含標頭檔案?所謂互相包含標頭檔案,我舉乙個例子:我實現了兩個類:
圖層類clayer
和符號類csymbol
,它們的大致關係是圖層裡包含有符號,符號裡定義乙個相關圖層指標,具體請參考如下**(注:以下**僅供說明問題,不作為類設計參考,所以不適宜以此討論類的設計,編譯環境為
microsoft visual c++ 2005,
,windows xp + sp2
,以下同):
現在開始編譯,編譯出錯,出錯資訊如下:
1>
正在編譯...
1>testunix.cpp
1>f:/mytest/mytest/src/testunix/symbol.h(14) : error c2143:
語法錯誤: 缺少「;」(在「*」的前面)
1>f:/mytest/mytest/src/testunix/symbol.h(14) : error c4430:
缺少型別說明符- 假定為int。注意: c++ 不支援預設int
1>f:/mytest/mytest/src/testunix/symbol.h(14) : error c4430:
缺少型別說明符- 假定為int。注意: c++ 不支援預設int
1>layer.cpp
1>f:/mytest/mytest/src/testunix/symbol.h(14) : error c2143:
語法錯誤: 缺少「;」(在「*」的前面)
1>f:/mytest/mytest/src/testunix/symbol.h(14) : error c4430:
缺少型別說明符- 假定為int。注意: c++ 不支援預設int
1>f:/mytest/mytest/src/testunix/symbol.h(14) : error c4430:
缺少型別說明符- 假定為int。注意: c++ 不支援預設int
1>symbol.cpp
1>f:/mytest/mytest/src/testunix/layer.h(18) : error c2143:
語法錯誤: 缺少「;」(在「*」的前面)
1>f:/mytest/mytest/src/testunix/layer.h(18) : error c4430:
缺少型別說明符- 假定為int。注意: c++ 不支援預設int
1>f:/mytest/mytest/src/testunix/layer.h(18) : error c4430:
缺少型別說明符- 假定為int。注意: c++ 不支援預設int
現在讓我們分析一下編譯出錯資訊(我發現分析編譯資訊對加深程式的編譯過程的理解非常有好處)。首先我們明確:
編譯器在編譯檔案時,遇到#
include "x.h"
時,就開啟
x.h檔案進行編譯,這相當於把
x.h檔案的內容放在
#include "x.h"
處。編譯資訊告訴我們:它是先編譯
testunix.cpp
檔案的,那麼接著它應該編譯stdafx.h
,接著是layer.h
,如果編譯layer.h
,那麼會編譯symbol.h
,但是編譯symbol.h又應該編譯layer.h啊,這豈不是陷入乙個死迴圈? 呵呵,如果沒有預編譯指令,是會這樣的,實際上在編譯symbol.h
,再去編譯layer.h
,layer.h
頭上的那個#pragma
once
就會告訴編譯器:老兄,這個你已經編譯過了,就不要再浪費力氣編譯了!那麼編譯器得到這個資訊就會不再編譯layer.h而轉回到編譯symbol.h的餘下內容。當編譯到clayer
*m_prellayer;這一行編譯器就會迷惑了:clayer
是什麼東西呢?我怎麼沒見過呢?那麼它就得給出一條出錯資訊,告訴你clayer沒經定義就用了呢?在testunix.cpp中#include
"layer.h"
這句算是宣告編譯結束(呵呵,簡單一句彎彎繞繞不斷),下面輪到#include
"symbol.h"
,由於預編譯指令的阻擋,symbol.h實際上沒有得到編譯,接著再去編譯testunix.cpp的餘下內容。
當然上面僅僅是我的一些推論,還沒得到完全證實,不過我們可以稍微測試一下,假如在testunix.cpp將#include
"layer.h"
和#include
"symbol.h"
互換一下位置,那麼會不會先提示csymbol
類沒有定義呢?實際上是這樣的。當然這個也不能完全證實我的推論。
然後編譯,出現乙個編譯警告:
>f:/mytest/mytest/src/testunix/layer.cpp(16) : warning c4150:
刪除指向不完整「csymbol」型別的指標;沒有呼叫析構函式
看到這個警告,我想你一定悟到了什麼。下面我說說我的結論:
類的前置宣告和包含標頭檔案
的區別在於
類的前置宣告
是告訴編譯器有這種型別,但是它沒有告訴編譯器這種型別的大小、成員函式和資料成員,而
包含標頭檔案
則是完全告訴了編譯器這種型別到底是怎樣的(包括大小和成員)。這下我們也明白了為何前置宣告只能使用指標來進行,因為指標大小在編譯器是確定的。上面正因為前置宣告不能提供析構函式資訊,所以編譯器提醒我們:
「csymbol」型別的指標
是沒有呼叫析構函式
。如何解決這個問題呢?在
layer.cpp
加上#include
"symbol.h"
就可以消除這個警告。
對類前置宣告和包含標頭檔案的一點理解
對類前置宣告和包含標頭檔案的一點理解 類的前置宣告 forward declaration 和包含標頭檔案 include 的區別常常會迷惑我們,特別是涉及兩個類相互包含的時候。因此我們有必要搞清楚二者的區別以及二者的適用場合。首先我們需要問乙個問題是 為什麼兩個類不能互相包含標頭檔案?所謂互相包含...
類前置宣告和標頭檔案包含
類的前置宣告 forward declaration 和包含標頭檔案 include 的區別常常會迷惑我們,特別是涉及兩個類相互包含的時候。因此我們有必要搞清楚二者的區別以及二者的適用場合。首先我們需要問乙個問題是 為什麼兩個類不能互相包含標頭檔案?所謂互相包含標頭檔案,我舉乙個例子 圖層類clay...
Qt裡頭檔案包含的類的宣告
1 qt begin namespace 例如 ifndef mainwindow h define mainwindow h include qt begin namespaceclass qaction class qmenu class qtextedit qt end namespace在標...