首頁技術(shù)文章正文

學(xué)習(xí)C++之C++的復(fù)雜性

更新時(shí)間:2017-11-14 來源:黑馬程序員 瀏覽量:

C++的復(fù)雜性始終是一個(gè)不可回避的現(xiàn)實(shí)。C++中有大量的陷阱和缺陷,后者導(dǎo)致了數(shù)目驚人的慣用法和workarounds。不加選擇的全盤預(yù)先學(xué)習(xí),是非常糟糕的做法,不僅低效,而且根本沒有必要,實(shí)在是浪費(fèi)生命。愛因斯坦曾經(jīng)說過,“我只想知道‘他’(宇宙)的設(shè)計(jì)理念,其它的都是細(xì)節(jié)”。然而,正如另一些讀者指出的,如果對(duì)C++中的這些細(xì)節(jié)事先一點(diǎn)都沒有概念的話,那么實(shí)際編碼中一旦遇到恐怕就變成沒頭蒼蠅了,也許到哪里去RTFM都不知道。這也是為什么那么多C++面試都會(huì)不厭其煩地問一些有代表性的語言細(xì)節(jié)的原因。

把細(xì)節(jié)全盤裝在腦子里固然不好,但對(duì)細(xì)節(jié)一無所知同樣也不是個(gè)辦法。那么對(duì)于C++程序員來說,在學(xué)習(xí)中究竟應(yīng)該以怎樣的態(tài)度和學(xué)習(xí)方法來對(duì)付C++的復(fù)雜性呢?其實(shí)答案也非常簡單,首先有一些很重要&必須的語言細(xì)節(jié)&特性是需要掌握的,然后我們只需知道在C++中大抵有哪些地方有復(fù)雜性(陷阱、缺陷),那么遇到問題的時(shí)候自然能夠知道到哪兒去尋找答案了。

C++的復(fù)雜性分類

這里并不詳細(xì)羅列C++的復(fù)雜性,而是提供一個(gè)分類標(biāo)準(zhǔn)。C++的復(fù)雜性有兩種分類辦法,一是分為非本質(zhì)復(fù)雜性和本質(zhì)復(fù)雜性;其中非本質(zhì)復(fù)雜性分為缺陷和陷阱兩類。另一種分類辦法是按照?qǐng)鼍胺诸悾簬扉_發(fā)場景下的復(fù)雜性和日常編碼的復(fù)雜性。從從事日常編碼的實(shí)踐者的角度來說,采用后一種分類可以讓我們迅速掌握80%場景下的復(fù)雜性。

二八法則

以下通過列舉一些常見的例子來解釋這種分類標(biāo)準(zhǔn):

80%場景下的復(fù)雜性:

1. 資源管理(C++日常復(fù)雜性的最主要來源):深拷貝&淺拷貝;類的四個(gè)特殊成員函數(shù);使用STL;RAII慣用法;智能指針等等。

2. 對(duì)象生命期:局部&全局對(duì)象生存期;臨時(shí)對(duì)象銷毀;對(duì)象構(gòu)造&析構(gòu)順序等等。

3. 多態(tài)

4. 重載決議

5. 異常(除非你不用異常):棧開解(stack-unwinding)的過程;什么時(shí)候拋出異常;在什么抽象層面上拋出異常等等。

6. undefined&unspecified&implementation defined三種行為的區(qū)別:i++ + ++i是undefined behavior(未定義行為——即“有問題的,壞的行為,理論上什么事情都可能發(fā)生”);參數(shù)的求值順序是unspecified(未指定的——即“你不能依賴某個(gè)特定順序,但其行為是良好定義的”);當(dāng)一個(gè)double轉(zhuǎn)換至一個(gè)float時(shí),如果double變量的值不能精確表達(dá)在一個(gè)float中,那么選取下一個(gè)接近的離散值還是上一個(gè)接近的離散值是implementation defined(實(shí)現(xiàn)定義的——即“你可以在實(shí)現(xiàn)商的編譯器文檔中找到說明”)。這些問題會(huì)影響到你編寫可移植的代碼。

(注:以上只是一個(gè)不完全列表,用于演示該分類標(biāo)準(zhǔn)的意義——實(shí)際上,如果我們只考慮“80%場景下的復(fù)雜性”,記憶和學(xué)習(xí)的負(fù)擔(dān)便會(huì)大大減小。)

20%場景下的復(fù)雜性:

1. 對(duì)象內(nèi)存布局

2. 模板:偏特化;非類型模板參數(shù);模板參數(shù)推導(dǎo)規(guī)則;實(shí)例化;二段式名字查找;元編程等等。

3. 名字查找&綁定規(guī)則

4. 各種缺陷以及缺陷衍生的workarounds(C++書中把這些叫做“技術(shù)”):不支持concepts(boost.concept_check庫);類型透明的typedef(true-typedef慣用法);弱類型的枚舉(強(qiáng)枚舉慣用法);隱式bool轉(zhuǎn)換(safe-bool慣用法);自定義類型不支持初始化列表(boost.assign庫);孱弱的元編程支持(type-traits慣用法;tag-dispatch慣用法;boost.enable_if庫;boost.static_assert庫);右值缺陷(loki.mojo庫);不支持可變數(shù)目的模板參數(shù)列表(type-list慣用法);不支持native的alignment指定。

(注:以上只是一個(gè)不完全列表。你會(huì)發(fā)現(xiàn),這些細(xì)節(jié)或技術(shù)在日常編程中極少用到,尤其是各種語言缺陷衍生出來的workarounds,構(gòu)成了一個(gè)巨大的長尾,在無論是C++的書還是文獻(xiàn)中都占有了很大的比重,作者們稱它們?yōu)榧夹g(shù),然而實(shí)際上這些“技術(shù)”絕大多數(shù)只在庫開發(fā)當(dāng)中需要用到。)

非本質(zhì)復(fù)雜性&本質(zhì)復(fù)雜性

此外,考慮另一種分類辦法也是有幫助的,即分為非本質(zhì)復(fù)雜性和本質(zhì)復(fù)雜性。

非本質(zhì)復(fù)雜性(不完全列表)

1. 缺陷(指能夠克服的問題,但解決方案很笨拙;C++的書里面把克服缺陷的workarounds稱作技術(shù),我覺得非常誤導(dǎo))。

2. 陷阱(指無法克服的問題,只能小心繞過;如果跌進(jìn)去,那就意味著你不知道這個(gè)陷阱,那么很大可能性你也不知道從哪去解決這個(gè)問題):一般來說,作為一個(gè)合格的程序員(不管是不是C++程序員),80%場景下的語言陷阱是需要記住才行的。比如深拷貝&淺拷貝;基類的析構(gòu)函數(shù)應(yīng)當(dāng)為虛;缺省生成的類成員函數(shù);求值順序&序列點(diǎn);類成員初始化順序&聲明順序;導(dǎo)致不可移植代碼的實(shí)現(xiàn)相關(guān)問題等。

本質(zhì)復(fù)雜性(不完全列表)

1. 內(nèi)存管理

2. 對(duì)象生命期

3. 重載決議

4. 名字查找

5. 模板參數(shù)推導(dǎo)規(guī)則

6. 異常

7. OO(動(dòng)態(tài))和GP(靜態(tài))兩種范式的應(yīng)用場景和交互

總而言之,該文的目的是要告訴你從一個(gè)較高的層次去把握C++中的復(fù)雜性。其中最重要的一個(gè)指導(dǎo)思想就是在學(xué)習(xí)的過程中注意你正學(xué)習(xí)的技術(shù)或細(xì)節(jié)到底是80%場景下的還是20%場景下的,如果是20%場景下的(有大量這類復(fù)雜性,其中尤數(shù)各種各樣的workarounds為巨),那么也許最好的做法是只記住一個(gè)大概,不去作任何深究。此外,一般來說,不管使用哪門語言,認(rèn)識(shí)語言陷阱對(duì)于編程來說都是一個(gè)必要的條件,語言陷阱的特點(diǎn)是如果你掉進(jìn)去了,那么很大可能意味著你本來就不知道這有個(gè)陷阱,后者很大可能意味著你不知道如何解決。友情提示:獲得更多學(xué)科學(xué)習(xí)視頻+資料+源碼,請(qǐng)加QQ:3276250747。


本文版權(quán)歸黑馬程序員C/C++學(xué)院所有,歡迎轉(zhuǎn)載,轉(zhuǎn)載請(qǐng)注明作者出處。謝謝!


作者:黑馬程序員C/C++培訓(xùn)學(xué)院


首發(fā):http://c.itheima.com/


分享到:
在線咨詢 我要報(bào)名
和我們?cè)诰€交談!