前置声明相关
一個前置聲明是指在程序員尚未給出完整定義之前對一個標(biāo)示符(一個類型、一個變量或者一個函數(shù))的聲明。一個很簡單的例子就是我們在函數(shù)A中使用了函數(shù)B,但是函數(shù)B的聲明在函數(shù)A之后,這個時候,就需要對函數(shù)B進(jìn)行前置聲明,實(shí)際上就是在函數(shù)A之前提供一個函數(shù)B的原型(prototype)。這種現(xiàn)象其實(shí)在C語言編程中我們已經(jīng)習(xí)以為常了,在C++中亦是如此,只不過在編寫較大規(guī)模程序的時候,由于定義了較多的類,而這些類之間有可能是互相依賴的,換言之,類與類之間會互相引用,包括對成員函數(shù)的引用等等。
比如需要在文檔類調(diào)用視圖類
在doc頭文件加前置聲明
class CTestView;
如有必要在doc源文件加上testview.cpp(或者加在頭文件也可),
在testview也是如此,在頭文件
加前置聲明
class CTestDoc;
源文件加testdoc.cpp(或者加在頭文件也可)
一、類嵌套的疑問
C++頭文件重復(fù)包含實(shí)在是一個令人頭痛的問題,前一段時間在做一個簡單的數(shù)據(jù)結(jié)構(gòu)演示程序的時候,不只一次的遇到這種問題。假設(shè)我們有兩個類A和B,分別定義在各自的有文件A.h和B.h中,但是在A中要用到B,B中也要用到A,但是這樣的寫法當(dāng)然是錯誤的:
復(fù)制到剪貼板??C/C++代碼 [cpp]?view plaincopy因?yàn)樵贏對象中要開辟一塊屬于B的空間,而B中又有A的空間,是一個邏輯錯誤,無法實(shí)現(xiàn)的。在這里我們只需要把其中的一個A類中的B類型成員改成指針形式就可以避免這個無限延伸的怪圈了。為什么要更改A而不是B?因?yàn)榫退隳阍贐中做了類似的動作,也仍然會編譯錯誤,表面上這僅僅上一個先后順序的問題。
為什么會這樣呢?因?yàn)镃++編譯器自上而下編譯源文件的時候,對每一個數(shù)據(jù)的定義,總是需要知道定義的數(shù)據(jù)的類型的大小。在預(yù)先聲明語句class B;之后,編譯器已經(jīng)知道B是一個類,但是其中的數(shù)據(jù)卻是未知的,因此B類型的大小也不知道。這樣就造成了編譯失敗,VC++6.0下會得到如下編譯錯誤:
error C2079: ‘b’ uses undefined class ‘B’
將A中的b更改為B指針類型之后,由于在特定的平臺上,指針?biāo)嫉目臻g是一定的(在Win32平臺上是4字節(jié)),這樣可以通過編譯。
二、不同頭文件中的類的嵌套
在實(shí)際編程中,不同的類一般是放在不同的相互獨(dú)立的頭文件中的,這樣兩個類在相互引用時又會有不一樣的問題。重復(fù)編譯是問題出現(xiàn)的根本原因。為了保證頭文件僅被編譯一次,在C++中常用的辦法是使用條件編譯命令。在頭文件中我們常常會看到以下語句段(以VC++6.0自動生成的頭文件為例):
#if !defined(AFX_STACK_H__1F725F28_AF9E_4BEB_8560_67813900AE6B__INCLUDED_)
#define AFX_STACK_H__1F725F28_AF9E_4BEB_8560_67813900AE6B__INCLUDED_
//很多語句……
#endif
其中首句#if !defined也經(jīng)常做#ifndef,作用相同。意思是如果沒有定義過這個宏,那么就定義它,然后執(zhí)行直到#endif的所有語句。如果下次在與要這段代碼,由于已經(jīng)定義了那個宏,因此重復(fù)的代碼不會被再次執(zhí)行。這實(shí)在是一個巧妙而高效的辦法。在高版本的VC++上,還可以使用這個命令來代替以上的所有:
#pragma once
它的意思是,本文件內(nèi)的代碼只被使用一次。
但是不要以為使用了這種機(jī)制就全部搞定了,比如在以下的代碼中:
C/C++代碼 [cpp]?view plaincopy
?
這里兩者都使用了指針成員,因此嵌套本身不會有什么問題,在主函數(shù)前面使用#include “A.h”之后,主要編譯錯誤如下:
error C2501: ‘A’ : missing storage-class or type specifiers
仍然是類型不能找到的錯誤。其實(shí)這里仍然需要前置聲明。分別添加前置聲明之后,可以成功編譯了。代碼形式如下:
這樣至少可以說明,頭文件包含代替不了前置聲明。有的時候只能依靠前置聲明來解決問題。我們還要思考一下,有了前置聲明的時候頭文件包含還是必要的嗎?我們嘗試去掉A.h和B.h中的#include行,發(fā)現(xiàn)沒有出現(xiàn)新的錯誤。那么究竟什么時候需要前置聲明,什么時候需要頭文件包含呢?
三、兩點(diǎn)原則
頭文件包含其實(shí)是一想很煩瑣的工作,不但我們看著累,編譯器編譯的時候也很累,再加上頭文件中常常出現(xiàn)的宏定義。感覺各種宏定義的展開是非常耗時間的,遠(yuǎn)不如自定義函數(shù)來得速度。我僅就不同頭文件、源文件間的句則結(jié)構(gòu)問題提出兩點(diǎn)原則,僅供參考:
第一個原則應(yīng)該是,如果可以不包含頭文件,那就不要包含了。這時候前置聲明可以解決問題。如果使用的僅僅是一個類的指針,沒有使用這個類的具體對象(非指針),也沒有訪問到類的具體成員,那么前置聲明就可以了。因?yàn)橹羔樳@一數(shù)據(jù)類型的大小是特定的,編譯器可以獲知。
?
第二個原則應(yīng)該是,盡量在CPP文件中包含頭文件,而非在頭文件中。假設(shè)類A的一個成員是是一個指向類B的指針,在類A的頭文件中使用了類B的前置聲明并便宜成功,那么在A的實(shí)現(xiàn)中我們需要訪問B的具體成員,因此需要包含頭文件,那么我們應(yīng)該在類A的實(shí)現(xiàn)部分(CPP文件)包含類B的頭文件而非聲明部分(H文件)。
posted on 2013-07-21 16:05 zhanzc 閱讀(...) 評論(...) 編輯 收藏轉(zhuǎn)載于:https://www.cnblogs.com/jameszhan/p/declare.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
- 上一篇: (转)代理模式(Proxy)
- 下一篇: [导入]画带阴影效果的文字