C++ Programming language读书笔记
C語言,結(jié)構(gòu)化程序設(shè)計(jì)。自頂向下、逐步求精及模塊化的程序設(shè)計(jì)方法;使用三種基本控制結(jié)構(gòu)構(gòu)造程序,任何程序都可由順序、選擇、循環(huán)三種基本控制結(jié)構(gòu)構(gòu)造。
模塊結(jié)構(gòu):"獨(dú)立功能,單出、入口"
C++ :是C的超集,類,封裝,重載,集成。
? 通用程序設(shè)計(jì):容器, vector,list,dequeue,map, set
? ? ? ? ? ? ? ? ? ? ? 算法:排序,查找,,復(fù)制,合并,插入,刪除
? ?模板類, STL
?
?
http://blog.163.com/leonary_dy/blog/static/405528602009122103416862/
?
雖然很多人一再強(qiáng)調(diào)語言細(xì)節(jié)不重要,可我還是要花時(shí)間重讀經(jīng)典。上次我認(rèn)認(rèn)真真讀這本書還要追溯到兩年前,現(xiàn)在我對(duì)C++的理解更深了一層,可以從書中讀到一些兩年前無法領(lǐng)悟的東西。
聲明:本筆記只記錄我以前不了解的部分,請(qǐng)初學(xué)者不要作為書籍摘要
我看的是TCPL電子版第三版,June 1997第一次印刷。這是網(wǎng)上最常見的版本,前兩頁書皮雖然寫著special版,其實(shí)后面版權(quán)信息那里寫的是第三版,書皮應(yīng)該是有人后加上去的。電子書雖便宜,不過因?yàn)槭浅醢?#xff0c;錯(cuò)誤在所難免,類似auto_ptr的錯(cuò)誤這種地方(詳見下面鏈接)。如果以前沒看過這本書,我建議買一本新版的紙質(zhì)書,從權(quán)威、厚度、質(zhì)量 來看性價(jià)比都遠(yuǎn)超Effective?系列
http://blog.163.com/leonary_dy/blog/static/40552860200891123358999/
2009-2-22
前面三章隨便翻翻跳過,太基礎(chǔ)了
4.4?節(jié)開頭,“把unsigned int作為一個(gè)(32位的:我注)bit數(shù)組很合適,但是為了獲得更大的正數(shù)范圍而使用無符號(hào)整型并不是好辦法,這可能會(huì)因?yàn)殡[式類型轉(zhuǎn)換帶來一些莫名其妙的問題”。其實(shí)這是有道理的,畢竟最大的unsigned int比signed int只大一倍而已,如果signed int可能溢出,unsigned同樣很危險(xiǎn)。可是標(biāo)準(zhǔn)庫里面用的size_t公然定義為unsigned int,給代碼編譯帶來了N多warning,不爽啊不爽
0開頭的表示八進(jìn)制,0x開頭表示16進(jìn)制
4.6節(jié),BS爺爺說char至少有8bit,我分明記得NetMM在水木說過標(biāo)準(zhǔn)規(guī)定的下限是7bit,鑒于此問題不嚴(yán)重,哪天無聊了再去翻翻最新的標(biāo)準(zhǔn)吧。wchar_t在C當(dāng)中是一個(gè)宏,C++當(dāng)中是一個(gè)關(guān)鍵字。在VC9當(dāng)中有一個(gè)編譯選項(xiàng)可以開關(guān)
頭文件<limits>中可以找到各種上下限
枚舉類型是有長度限制的,對(duì)其做算術(shù)運(yùn)算的時(shí)候需要留意一下。不過我在VC9上試了一下,/01?參數(shù)編譯出來的一個(gè){0,1}?枚舉sizeof是4,/0d?當(dāng)然也是4了。
4.9.3?講了一段命名規(guī)則的問題,使用范圍廣的名字應(yīng)該長一些,很local的名字應(yīng)該短一些,或許我們可以考慮通過某個(gè)名字的使用范圍和頻率的哈夫曼編碼確定其長度進(jìn)行重構(gòu)
4.9.6 Objects和左值,這一段有些莫名其妙,兩者沒有太大關(guān)系呀
附錄C3.4的最后結(jié)論我并不同意,全部使用char來避免char之間的賦值轉(zhuǎn)換并不是好辦法,移植的時(shí)候還是char的不確定性帶來的問題更多。實(shí)際上還是應(yīng)該徹底避免用char進(jìn)行運(yùn)算,在java游戲的時(shí)代內(nèi)存很緊張,不得不省吃儉用,遺留代碼中落下了很多不必要的麻煩。
5.6節(jié)最后?"Pointers to functions (§7.7) and pointers to members (§15.5) cannot be assigned to void*s."?為什么?為什么??
2009-2-24
6.1?節(jié)BS為我們展示一個(gè)計(jì)算器,實(shí)質(zhì)上是一個(gè)詞法分析器,結(jié)構(gòu)的層次化很嚴(yán)謹(jǐn),從語法的角度來講我看這段代碼應(yīng)該富富有余,但是實(shí)際上沒那么簡單。如何把一個(gè)繁瑣并不復(fù)雜的東西寫的簡潔清晰,這需要深厚的功底,這類的東西我向來是比較頭大的。
6.2.2?節(jié)提到了賦值順序的問題,這其實(shí)是一個(gè)很嚴(yán)肅的問題,BS說類似
v[i] = i++;
這樣的代碼編譯器應(yīng)該作出警告,但是我把VC9開到W4也沒有警告。
, && ||?這三個(gè)操作符可以保證左操作數(shù)的賦值先于右操作數(shù),這其實(shí)是一個(gè)序列點(diǎn)概念(sequence point),BS在這里沒有進(jìn)一步的展開講解,考慮到這本書的定位和目的。操作符優(yōu)先級(jí)曾經(jīng)是N多腦殘考官的最愛,大多數(shù)都不是問題,例外在于&&?和?||?之間是有順序的,并不像我以前想象的那樣同級(jí)關(guān)系從左至右依次計(jì)算。在a || b && c的時(shí)候,該加括號(hào)的地方要加括號(hào)。
2009-2-25
6.2.8節(jié),提到了?T(v)?相當(dāng)于static_cast<T>(e),而上面講的(T)e?則是根據(jù)上下文選擇三種cast之中的一種(不會(huì)dynamic_cast),我一度還認(rèn)為T(v)?和(T) v?是等價(jià)的
6.3.3 BS爺爺說他也跟我一樣不喜歡do-while循環(huán)
7.2.1節(jié)又是輕描淡寫的提到了一個(gè)很詭異的地方,和參數(shù)類型不一致的常量在參數(shù)傳遞的時(shí)候可以綁定到const引用類型的臨時(shí)變量上,這是臨時(shí)變量的用途之一,涉及到臨時(shí)變量的生命周期、隱式類型轉(zhuǎn)換等問題。
前兩天發(fā)現(xiàn)臨時(shí)對(duì)象的一個(gè)問題: 對(duì)于T a = v ;??在C++標(biāo)準(zhǔn)當(dāng)中要求語義上生成一個(gè)臨時(shí)對(duì)象再拷貝構(gòu)造a,而不是T a(0)這種直接構(gòu)造a。然而VC擴(kuò)展當(dāng)中不檢查T的拷貝構(gòu)造函數(shù),即使拷貝構(gòu)造的參數(shù)非const也可以編譯通過。打開/za選項(xiàng)就是按照標(biāo)準(zhǔn)的說法給出error
隱式類型轉(zhuǎn)換當(dāng)中的類型提升不包括int=》long的轉(zhuǎn)換,需要注意。BS爺爺在前面說過如果沒事只用int做整型就好,除非歷史遺留問題,不要用short long之類
2009-2-28
8.2.?類名也是namespace的一種,由于類的聲明、定義先入為主,導(dǎo)致后來我接觸namespace時(shí)在這一點(diǎn)上我的理解不夠深。namespace?定義的scope和class定義的scope有很多相似之處,聲明必須在scope標(biāo)記的?{}?當(dāng)中,定義可以用namespace::name?形式。同一scope內(nèi)的名字引用時(shí)不需要namespace::?前綴
using namespace xxxxxx;?是個(gè)不太好的習(xí)慣,失去了namespace應(yīng)有的意義
using xxxxspace::xxxx;才是正確的做法。
BS在8.2.3節(jié)肯定了我的看法,但是很遺憾我們現(xiàn)在的代碼中充斥了這樣的用法,也許對(duì)于一個(gè)十萬行量級(jí)的項(xiàng)目來說,這種用法帶來的方便足以抵消引入的問題。在8.2.8節(jié)寫到:
理想情況下,namespace應(yīng)該:
1 xxxxx
2 xxxxx
3?不應(yīng)該讓用戶使用太過繁瑣
如果很繁瑣,那實(shí)際上暴露出設(shè)計(jì)上的缺陷
8.2.6?很通俗、簡要的介紹了ADL(Argument Dependent Lookup),也叫做Keoniglookup,在C++標(biāo)準(zhǔn)當(dāng)中唯一一個(gè)以人名命名的規(guī)則,Andrew Koenig。這個(gè)規(guī)則實(shí)際上在模板部分有著更為深刻的應(yīng)用背景,但在本節(jié)沒辦法講的那么復(fù)雜。詳細(xì)情況請(qǐng)自行谷歌,譬如下面這個(gè)
http://blog.pfan.cn/vfdff/35154.html
2009-3-4
最近兩天看的東西開始讓我懷疑以前究竟有沒有讀過TCPL,很多明明在書上寫著的東西我到現(xiàn)在才知道
9.2?節(jié)講了Linkage的一些規(guī)則,9.2.1小標(biāo)題前面提了一句說在C和早期的C++當(dāng)中static的意思是“internal linkage”,新寫代碼就不要把static這樣用了。非extern的const變量就可以保證internal linkage,如果需要變量可以使用匿名的namespace,當(dāng)然使用全局變量這本身就是一個(gè)很土的做法,很容易被人鄙視的。水木精華區(qū)x-5-6-38提到匿名namespace?中的變量是external linkage
http://www.newsmth.net/bbsanc.php?path=%2Fgroups%2Fcomp.faq%2FCPlusPlus%2Ffaq%2Flinker%2FM.1149790415.l0
ODR(one-definition-rule),本應(yīng)該規(guī)定自定義類型、模板在一個(gè)程序中只能定義一次。但是由于頭文件的問題,這個(gè)規(guī)則在C++當(dāng)中就變成了同一類型的定義在一個(gè)編譯單元中只有一次,在不同編譯單元中必須一致。
2009-3-7
10.2.4節(jié) 靜態(tài)成員變量可以是自身類型,也就是說:
class Date{
static Date default_data;
};
這樣的代碼看起來很新穎,我以前知道的只有通過指針嵌套,不過這種用法的使用范圍似乎沒有指針那么多。
10.4.6.3?節(jié)“Note that the default copy constructor leaves a reference member referring to the same object in both the original and the copied object.? This can be a problem if the object referred to is supposed to be deleted.”??就是我前面博客中寫的“關(guān)于引用類型的成員變量”
http://blog.163.com/leonary_dy/blog/static/40552860200871811858224/
看到這里的時(shí)候我應(yīng)該感嘆英雄所見略同,還是自己以前讀TCPL太過膚淺呢。
10.4.9節(jié)最后講的使用一個(gè)靜態(tài)函數(shù)初始化,通過一個(gè)靜態(tài)變量標(biāo)記是否已經(jīng)初始化。可是這句話“the really difficult case is the one in which the first operation may be time- critical so that the overhead of testing and possible initialization can be serious.”???我沒看明白這句話說的是什么問題,以及21.5.2相比10.4.9這個(gè)辦法優(yōu)越在哪里。留待以后再說吧,看了一下VC的初始化全局變量cin?的時(shí)候使用的也是編譯器提供的特性,21.5.2也說編譯器的實(shí)現(xiàn)要更為簡單可靠。
11.2.4?最后這里的描述似乎有些問題,成員函數(shù)的操作符重載和全局函數(shù)的操作符重載是可以ambiguous的,并不存在成員函數(shù)高于全局函數(shù)的問題。并且一視同仁也避免了同名函數(shù)之間的“隱藏”。
但是有一組operator是例外,那就是new和delete系列,成員operator new和delete的優(yōu)先級(jí)高于全局的new、delete,內(nèi)存分配操作符還有一點(diǎn)與眾不同之處在于他們都是靜態(tài)的,即使沒有顯式static聲明。
11.3.1 操作符操作成員的才是成員函數(shù) (*=. +=), ? (+ - * / 這些不必,可以弄到全局)
11.3.4?節(jié),為什么拷貝構(gòu)造的參數(shù)必須是引用? 因?yàn)閰?shù)傳遞本身就會(huì)涉及到一次拷貝,如果不是引用類型那拷貝構(gòu)造將成為一個(gè)無限遞歸調(diào)用!!
下面關(guān)于complex x=2;的描述可能有些問題,詳見水木C++版111文,太細(xì)節(jié),不看也罷
11.4 Conversion Operators?隱式類型轉(zhuǎn)換,比較容易出問題的一個(gè)地方,但是用于寫一些底層工具的話又非常方便,也不可避免,用的時(shí)候需要多加小心。11.3.5最后提到成員操作符只 能用于左值變量,并不會(huì)通過隱式類型轉(zhuǎn)換生成一個(gè)臨時(shí)對(duì)象,這大約是為了避免隱式類型轉(zhuǎn)換不小心帶來的問題吧。
2009-3-14
11.4.1?最后的那個(gè)例子,可以實(shí)現(xiàn)某種程度上的按返回值重載函數(shù),詳情見RC的這個(gè)帖子
http://www.newsmth.net/bbsanc.php?path=%2Fgroups%2Fcomp.faq%2FCPlusPlus%2Fcodeandtrick%2FM.1037702476.F0
11.5.1
作為類函數(shù):
1.訪問類的私有部分
2.在類的作用域
3.經(jīng)由對(duì)象指針去操作。
友元只有前兩項(xiàng).
?最后那個(gè)例子似乎有些問題
class?X {
friend?void?f();
int?i;
};
?
void?f() {
X a;
a.i = 0;
}
TCPL上說f()?必須要在聲明為友元之前聲明一次,否則不視為X的友元函數(shù),也就是說a.i?=0那里應(yīng)該編譯錯(cuò)誤。但是我查了一下標(biāo)準(zhǔn)中friend一節(jié)并未提及這一限制,在VC9和Comeau 4.3.10上編譯也沒有遇到問題。
?
可以進(jìn)行隱式類型轉(zhuǎn)換,作為全局的, 類的成員函數(shù),不能運(yùn)行對(duì)象進(jìn)行轉(zhuǎn)換 clas A{ A(int a); print()}; 12.print不行,不能進(jìn)行A(12).print()的轉(zhuǎn)換。
可以定義全局的,如果再需要訪問類的內(nèi)部,定義友元。
必須是成員函數(shù)的是,構(gòu)造函數(shù),析構(gòu) 和虛函數(shù)
?
?
12.4.2 “Since Ival_box provides the interface for the derived class, it is derived
using public. Since BBwindow is only an implementation aid, it is derived using protected ”
這里解釋了什么時(shí)候應(yīng)該public繼承,什么時(shí)候應(yīng)該protected繼承。protected繼承和private繼承的時(shí)候外界是無法通過子類對(duì)象訪問其父類成員的,同時(shí)從子類向父類的指針、引用的隱式類型轉(zhuǎn)換也被禁止。
?
2009-3-21
進(jìn)入第13章,發(fā)現(xiàn)腦細(xì)胞有些不夠用。模板和繼承是C++中代碼復(fù)用的兩大法寶,很重要的一點(diǎn)是需要搞清楚什么場合用什么技術(shù)。13.6.1簡單的分析了一下“我們操作一堆具有共同屬性的對(duì)象時(shí),如果這些對(duì)象之間有某種繼承關(guān)系,就應(yīng)該使用虛函數(shù)完成,反之應(yīng)該使用模板。”這一段是我進(jìn)入13章 以來第一次記起來上次曾經(jīng)看過,前面的特化、偏特化、模板繼承的用法通通沒有印象了。但是這也應(yīng)該是使用模板最為關(guān)鍵的一個(gè)問題了,在現(xiàn)實(shí)當(dāng)中,我目前的 做法是:沒有很充分的理由需要使用模板的時(shí)候,優(yōu)先考慮通過繼承的方式抽象。模板帶來的可讀性上的代價(jià)必須要考慮進(jìn)去。對(duì)于我來說,同樣的一段代碼用模板 表達(dá)出來要比用繼承表達(dá)要難懂的多。也許隨著水平的提高模板相對(duì)繼承的使用成本會(huì)降低一些。
?
這一部分無疑是C++當(dāng)中最耗費(fèi)腦細(xì)胞的環(huán)節(jié),完全沒有前面幾章一馬平川的感覺。我不得不放棄了前面一邊看一邊寫評(píng)語的習(xí)慣,只能先看過一遍再返回來慢慢理解。
13.2.3節(jié)模板參數(shù)可以是模板,還可以是常量,通過常量表達(dá)式或external linkage的對(duì)象/函數(shù)的地址表達(dá)。常量參數(shù)一個(gè)很強(qiáng)大的武器,通過模板函數(shù)的重載、遞歸某些吃飽了撐著的人開發(fā)了各種各樣的編譯期運(yùn)算的tricks,這里僅舉一個(gè)小例子,一個(gè)可以返回任意類型數(shù)組元素個(gè)數(shù)的函數(shù)
template<class?T,?int?n>
int?Func(T (&X)[n])
{
return?n;
}
更多的例子可以去boost里面找,或者看看那本什么模板元編程的書
?
13.3.2節(jié) 在函數(shù)重載的時(shí)候普通函數(shù)比一個(gè)模板函數(shù)的優(yōu)先級(jí)要高。在進(jìn)行模板參數(shù)推導(dǎo)的時(shí)候,涉及推導(dǎo)的參數(shù)是不允許進(jìn)行隱式類型轉(zhuǎn)換的。但是不涉及推導(dǎo)的部分可以轉(zhuǎn)化譬如本節(jié)最后的例子:
template<class T> class B{};
template<class T> classD : public B<T> {};
template<class T>void Func( B<T>*);//?這里可以進(jìn)行D<int>*到B<int>*的轉(zhuǎn)換
//?只要能推導(dǎo)出一個(gè)確定的T類型即可
?
與之相反的例子在13.6.3節(jié),模板和類的繼承兩者混用會(huì)導(dǎo)致嚴(yán)重的問題,一個(gè)是運(yùn)行時(shí)一個(gè)是編譯期重用。D是B的子類,D*可以隱式轉(zhuǎn)換到B*并不意味著vector<D*>可以隱式轉(zhuǎn)換到vector<B*>。如果有這種需求,我們可以使用13.6.2節(jié)給出的帶模板的構(gòu)造函數(shù)來進(jìn)行轉(zhuǎn)換。
?
13.4.1節(jié),這里書上出現(xiàn)了一個(gè)比較大的問題,模板參數(shù)的默認(rèn)值只能用于構(gòu)建模板類,而不能用于模板函數(shù)或者模板類的成員函數(shù),也就是說本節(jié)這個(gè)compare函數(shù)是編不過的。在VC9上編譯時(shí)報(bào)告error C4519,查了一下潘愛民版的依然存在這個(gè)問題。至于原因,我在這里找到了線索
http://www.open-std.org/JTC1/sc22/wg21/docs/cwg_defects.html#226
BS爺爺于2000年提交了一份提案,建議加上模板函數(shù)的默認(rèn)參數(shù),但是被否決了。原因是參數(shù)可以推導(dǎo)、函數(shù)可以重載,如果還可以默認(rèn)類型的話問題將變得非常復(fù)雜。
?
13.5?模板特化有一個(gè)順序問題需要注意一下。如果某個(gè)模板在特化的聲明之前已經(jīng)進(jìn)行過實(shí)例化,會(huì)產(chǎn)生一個(gè)編譯錯(cuò)誤。如果在偏特化之前進(jìn)行過實(shí)例化,VC9當(dāng)中可編譯通過,按照未偏特化的版本進(jìn)行實(shí)例化。
另外GCC實(shí)例化的時(shí)候還有一個(gè)不討人喜歡的"feature",pragma pack按照實(shí)例化位置的pack進(jìn)行pack,而不是定義模板類的pack,具體情況看這里:
http://chen3feng.spaces.live.com/blog/cns!FEF0D083246BBED0!1880.entry
因?yàn)閷?shí)例化可能發(fā)生在各種地方,這種順序帶來的問題可能還有。
?
?
13.5.1最后那句話翻來覆去看不懂…… 另外還有兩個(gè)問題:
1.?模板函數(shù)是否可以偏特化?(ms不可以)
2.?剛才在哪兒看見一眼,模板函數(shù)的返回類型也是其signature的一部分,ms還可以通過返回值deduce模板參數(shù)(這是在標(biāo)準(zhǔn)上看到的)
?
13章大體上看完了兩遍,現(xiàn)在知道了不少可以怎么用的語法,但還有很多不知可不可以用的語法沒有搞清楚。
13.2
·通過某個(gè)實(shí)例化的模板來理解、調(diào)試代碼比較方便,不至于太抽象
·編譯器保證同樣的模板參數(shù)只產(chǎn)生一個(gè)實(shí)例化的類型/函數(shù)
·除了類型,還可以用幾種編譯期常量作為模板參數(shù)(但是string literal不可以)
13.3
·函數(shù)模板的參數(shù)可以在調(diào)用的時(shí)候做推導(dǎo)
13.5
·模板類可以通過偏特化+繼承減小生成的代碼量
?
2009-03-28
14章 異常處理
14.1.1 C++異常處理只用于同步的異常,硬件中斷等異步的異常不在考慮之內(nèi),譬如除以0等情況。
異常處理的設(shè)計(jì)原則是為了分離錯(cuò)誤檢測代碼和錯(cuò)誤處理代碼
通常情況下使用指針或引用類型catch異常,這樣不會(huì)在拷貝的時(shí)候丟失信息。
?
14.4.1就是大名鼎鼎的RAII原則,盡可能的利用構(gòu)造函數(shù)和析構(gòu)函數(shù)來管理資源的獲取和釋放,避免構(gòu)造函數(shù)拋出異常時(shí)未調(diào)用析構(gòu)函數(shù)造成的內(nèi)存泄漏等問題,這也就是通常所說的異常安全。
14.4.2 Auto_ptr這一節(jié)的電子版有問題,中文版部分糾正,詳情見我那篇《關(guān)于auto_ptr的三個(gè)問題》
?
問題:
?
譬如說標(biāo)準(zhǔn)庫當(dāng)中的異常,使用的代碼當(dāng)中沒有進(jìn)行catch,會(huì)出現(xiàn)未捕獲的異常嗎?(Yes)
聲明了throw類型的函數(shù)和沒聲明的函數(shù)是同一個(gè)函數(shù)嗎?如果不是的話如何重載?(必須保證同一函數(shù)的所有聲明、定義的Exception Specification一致,否則將是一個(gè)編譯錯(cuò)誤。但是這個(gè)限制比較雞肋,標(biāo)準(zhǔn)規(guī)定跨編譯單元的聲明檢查不是必須,否則嚴(yán)重影響編譯成本,因此在VC9當(dāng)中即使同一編譯單元默認(rèn)也不做這一檢查,打開/Za選項(xiàng)才認(rèn)為是一個(gè)編譯錯(cuò)誤)
?
14.6.3未捕獲異常的映射,在函數(shù)的exception specification中加一個(gè)std::bad_exception,如果出現(xiàn)指定以外的異常拋出,則自動(dòng)轉(zhuǎn)換為一個(gè)std::bac_exception而不是直接terminate()
?
14.11?建議,第四條,并不是每個(gè)程序都需要考慮異常安全,不要拿著錘子就想到處都敲一敲,KISS也很重要,一定要因地制宜,考慮實(shí)際情況編寫合適的代碼。
?
?
15.2.5
曾經(jīng)聽人說起在虛基類當(dāng)中不應(yīng)該放任何成員變量,但是原因并不能令人信服。這一節(jié)提到如果認(rèn)為虛繼承的性能開銷不可接受的話可以把不含有成員變量的虛基類改成非虛的,不會(huì)有任何問題。在設(shè)計(jì)C++的時(shí)候沒有把所有的基類繼承規(guī)定為虛的是出于歷史原因的考慮,不應(yīng)該為不需要的東西付出額外的開銷。現(xiàn)在來看,這個(gè)原因造成性能瓶頸的可能性太低了。如果有,這樣的應(yīng)用基本也還在用C,沒有轉(zhuǎn)移到C++陣營。
?
15.3.2
對(duì)于private繼承,子類向父類的指針轉(zhuǎn)換并非完全禁止,在類的內(nèi)部或者友元函數(shù)當(dāng)中還是允許的。
15.3.2.1?如果多重繼承當(dāng)中有多條路徑可以訪問某個(gè)父類的成員,則允許訪問的規(guī)則優(yōu)先于禁止訪問。
?
15.4.1?本節(jié)開始的例子指出dynamic_cast在downcast類型不對(duì)的時(shí)候返回空指針,在upcast訪問權(quán)限不夠的時(shí)候也返回空指針。不過我在VC里面試了一下,例子給出的那段代碼直接就給出了編譯錯(cuò)誤。這里可能是委員會(huì)后來作了修訂,upcast權(quán)限不足的直接給error,看下面的例子
class?Base{
};
class?D :?virtual?protected?Base{};
class?DD :?public?D,?virtual?public?Base
{};
?
?
int?main()
{
DD d;
D* pD = &d;
Base* pB =?dynamic_cast<Base*>(pD);
return?0;
}
因?yàn)槭撬接欣^承,通常情況下的D*是不可以轉(zhuǎn)換為Base*的,但是DD從Base公有繼承了一次,因此可以由DD*轉(zhuǎn)換為Base*。pD實(shí)際指向的是一個(gè)DD對(duì)象,也就是說按照RTTI的規(guī)則來看轉(zhuǎn)換為Base*比返回0更為合理。然而訪問權(quán)限這個(gè)東西其實(shí)只是一個(gè)編譯期的概念,并不存在于運(yùn)行期,如果到了運(yùn)行期是無法判斷出是否有足夠的訪問權(quán)限。但是在編譯期又無法決定pD究竟是一個(gè)D的指針還是DD的指針。
我能想到的就是這個(gè)原因,誰有興趣可以去翻一下C++標(biāo)準(zhǔn)或者提案,我不打算深究了。
btw:本節(jié)例子之前的那句話描述有些問題,應(yīng)該是這樣子" if p is of type T* or T is an accessible base class of p"
?
15.4.1?本節(jié)后面一個(gè)例子的trick有點(diǎn)兒意思,用多繼承的方式把一個(gè)concrete類型包在polymorphic類型當(dāng)中然后再轉(zhuǎn)回來,這是解決concrete類型不能dynamic_cast的一個(gè)workaround,看起來總比static_cast要類型安全一些。
?
15.4.2 dynamic_cast失敗除了因?yàn)轭愋筒粚?duì),還有可能是多繼承當(dāng)中virtual?和非virtual并存出現(xiàn)ambiguity。如果在upcast當(dāng)中出現(xiàn)這種情況,實(shí)際上在編譯期就可以知道的,我認(rèn)為完全沒必要搞到編譯期返回空指針,這個(gè)設(shè)計(jì)欠妥。
?
15.4.2.1?從virtual base做downcast不能用static_cast,既然dynamic_cast可以做這件事,既然你已經(jīng)不怕麻煩用了static,不妨改成dynamic吧。但有個(gè)問題,如果是非polymorphic基類被virtual繼承的話,那就兩種cast都搞不定了。當(dāng)然基類不polymorphic也是比較失敗的設(shè)計(jì),沒事不需要用罷了。
?
15.6.2?構(gòu)造函數(shù)不可以virtual,但是也有辦法作出同樣的效果,那就是本節(jié)代碼示范的通過virtual函數(shù)clone自身對(duì)象,這貌似是設(shè)計(jì)模式中的一種。
?
15.6 為什么如下虛函數(shù)沒有調(diào)用子類的呢
class PurvitualBase1{
public:
? ? ? ? virtual PurvitualBase1* new_expr(){return new PurvitualBase1();};
? ? ? ? virtual PurvitualBase1* clone(){return new PurvitualBase1(*this);};
private:
? ? ? ? int b;
};
class??PurvitualDeriv1: public??PurvitualBase1{
public:
? ? ? ? virtual PurvitualDeriv1* new_expr(){return new PurvitualDeriv1();};
? ? ? ? virtual PurvitualDeriv1* clone(){return new PurvitualDeriv1(*this);};
private:
? ? ? ? int bb;
};
void user2(PurvitualBase1 *pb, PurvitualDeriv1 *pd)
{
? ? ? ? PurvitualDeriv1 *pd2 = pd->clone();
? ? ? ? PurvitualDeriv1 *pd3 = pb->clone();
? ? ? ? pd2->output();
? ? ? ? pd3->output();
}
PurvitualDeriv1 testv1;
PurvitualBase1 *ptestpb1 = &testv1;
user2(ptestpb1, &testv1); //為什么這個(gè)函數(shù)調(diào)用時(shí),user2中的虛函數(shù)clone沒有用子類的clone,卻用了父類的clone呢?按理說應(yīng)該是虛函數(shù)調(diào)用子類的啊
轉(zhuǎn)載于:https://www.cnblogs.com/virusolf/p/5476616.html
總結(jié)
以上是生活随笔為你收集整理的C++ Programming language读书笔记的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 计算机正确的坐姿教案,礼仪课坐姿教案.d
- 下一篇: c++实现多项式类定义