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

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

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

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

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

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

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

二八法則

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

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

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

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

3. 多態(tài)

4. 重載決議

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

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

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

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

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

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

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

4. 各種缺陷以及缺陷衍生的workarounds(C++書中把這些叫做“技術(shù)”):不支持concepts(boost.concept_check庫);類型透明的typedef(true-typedef慣用法);弱類型的枚舉(強枚舉慣用法);隱式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指定。

(注:以上只是一個不完全列表。你會發(fā)現(xiàn),這些細(xì)節(jié)或技術(shù)在日常編程中極少用到,尤其是各種語言缺陷衍生出來的workarounds,構(gòu)成了一個巨大的長尾,在無論是C++的書還是文獻(xiàn)中都占有了很大的比重,作者們稱它們?yōu)榧夹g(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)去,那就意味著你不知道這個陷阱,那么很大可能性你也不知道從哪去解決這個問題):一般來說,作為一個合格的程序員(不管是不是C++程序員),80%場景下的語言陷阱是需要記住才行的。比如深拷貝&淺拷貝;基類的析構(gòu)函數(shù)應(yīng)當(dāng)為虛;缺省生成的類成員函數(shù);求值順序&序列點;類成員初始化順序&聲明順序;導(dǎo)致不可移植代碼的實現(xiàn)相關(guān)問題等。

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

1. 內(nèi)存管理

2. 對象生命期

3. 重載決議

4. 名字查找

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

6. 異常

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

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


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


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


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


分享到:
在線咨詢 我要報名
和我們在線交談!