C++知識分享:前置聲明及其解析
前置聲明是C/C++開發(fā)中比較常用的技巧,主要用在三種情形:
變量/常量,例如extern?int?var1;;
函數(shù),例如void?foo();,注意類的成員函數(shù)無法單獨做前置聲明;
類,例如class?Foo;,也可以前置聲明模板類:template?class<typename?T1,?int?SIZE>Foo;。如果類包含在名字空間中,需在名字空間內(nèi)做前置聲明:namespace?tlanyan?{class?Foo;};,而不能這樣:class?tlanyan::Foo;。

前置聲明作用
根據(jù)其用途,前置聲明的主要作用為:
(1)避免重復(fù)定義變量;
(2)避免引入函數(shù)定義/聲明文件,從而函數(shù)文件發(fā)生更改時不會重新編譯依賴文件;
(3)解決循環(huán)依賴問題。
前兩種用途好理解,第三種稍微復(fù)雜點,但卻是前置聲明最重要的用途。其解決類A包含類B,同時類B包含類A的依賴問題。循環(huán)依賴一般是設(shè)計層面的問題,可通過接口、引入輔助類等手段化解。前置聲明也能解決,只是架構(gòu)上稍微別扭。
不管A和B是否定義在同一個文件中,c++永遠(yuǎn)無法解決如下形式的循環(huán)依賴(后文解釋原因):

前置聲明解決該問題需要與指針配合,轉(zhuǎn)換成另一種形式。要點如下:
至少將某類的變量類型轉(zhuǎn)換成指針,例如A中將B轉(zhuǎn)成B*;
類A中對B使用前置聲明;
類A的定義文件中移除對類B文件的包含(做了包含保護(hù)則可忽略)。
使用前置聲明后,以下是一種可行的解決形式(兩個類均使用了前置聲明):

深入前置聲明
如果你有其他編程語言的經(jīng)驗,會發(fā)現(xiàn)c++有點怪異:Java/C#/Python/PHP等語言可以輕松做到循環(huán)引用,無需使用類似的前置聲明技巧。這不禁讓人思考:C++為何必須要用前置聲明才能化解?
原因在于C++定義對象有兩種方式:一種是A a形式,a即對象,調(diào)用成員變量或函數(shù)用.,對象在棧中分配;另一種是A*?a,a是指針,調(diào)用成員變量或函數(shù)用->,其指向地址存儲實際對象,對象在堆中分配。
分配對象需要知道具體的內(nèi)存大小,但以下形式我們不能確定類A和類B對象的大?。?/p>
對于這個簡單例子,你可以直觀認(rèn)為A和B占用同樣的內(nèi)存,例如1字節(jié),但也可以是2字節(jié),3字節(jié)等;根據(jù)內(nèi)存對齊要求,一般是4字節(jié),8字節(jié)等。無論哪種情況,編譯器無法確定其對象占用內(nèi)存,便會報錯停止編譯。所以你應(yīng)該知道為什么C++永遠(yuǎn)不應(yīng)該(不能)這樣做了吧?
那為何前置聲明加指針的組合能解決循環(huán)引用問題的呢?因為正常情況下,數(shù)據(jù)類型指針在同一機器的編譯器里占同樣的內(nèi)存。指針一般是4或者8個字節(jié),對應(yīng)32和64位指針。用了指針,即使有循環(huán)引用,類的大小也能輕易的確定下來。這也是Java/C#/Python/PHP等可以輕松循環(huán)引用的原因:這些語言中,對象變量其實都是指針,也意味著對象變量都是引用傳遞。
如果不移除文件的相互包含,能否省去前置聲明呢?答案是不能,原因如下:
1、C++按照一個個編譯單元(translation unit)進(jìn)行編譯,如果兩個文件互相包含且沒有#pragma?once等包含保護(hù)措施,則會出現(xiàn)遞歸包含,編譯器報錯;
2、如果兩個頭文件都有文件包含保護(hù),編譯A時會把B包含進(jìn)來,但因為B包含了A,A中的包含保護(hù)生效,導(dǎo)致B文件內(nèi)的內(nèi)容實際未引入A,于是報B為未知符號的錯誤。
總的來說,不管是否移除對方的頭文件,前置聲明都是必須的。實踐中為了避免文件變動時重新編譯的耗費,移除不必要的頭文件是一個好習(xí)慣。

那么今天的分享就到這里了,后續(xù)會更新更多精彩項目或者知識內(nèi)容的,大家要好好學(xué)C語言C++喲~
寫在最后:對于準(zhǔn)備學(xué)習(xí)C/C++編程的小伙伴,如果你想更好的提升你的編程核心能力(內(nèi)功)不妨從現(xiàn)在開始!
微信公眾號:C語言編程學(xué)習(xí)基地
整理分享(多年學(xué)習(xí)的源碼、項目實戰(zhàn)視頻、項目筆記,基礎(chǔ)入門教程)
歡迎轉(zhuǎn)行和學(xué)習(xí)編程的伙伴,利用更多的資料學(xué)習(xí)成長比自己琢磨更快哦!
