• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            Robin Chow's C++ Blog

             

            [導入]Effective C++讀書筆記

            條款1:盡量用const和inline而不用#define
            1.為方便調試,最好使用常量。
            注意:常量定義一般放在頭文件中,可將指針和指針所指的類型都定義成const,如const char * const authorName = “Scott Meyers”;
            類中常量通常定義為靜態成員, 而且需要先聲明后定義。可以在聲明時或定義時賦值,也可使用借用enum的方法。如enum{Num = 5};
            2.#define語句造成的問題。
            如#define max(a, b) ((a) > (b) ? (a) : (b))
            在下面情況下:
            Int a= 5, b = 0;
            max(++ a, b);
            max(++ a, b + 10);
            max內部發生些什么取決于它比較的是什么值。解決方法是使用inline函數,可以使用template來產生一個函數集。

            條款2:盡量用而不用
            用>> 和<<使得編譯器自己可以根據不同的變量類型選擇操作符的不同形式,而采取的語法形式相同。

            條款3:盡量用new和delete而不用malloc和free
            使用malloc和free的時候不會自己調用構造函數和析構函數,因此如果對象自己分配了內存的話,那么這些內存會全部丟失。另外,將new和malloc混用會導致不可預測的后果。

            條款4:盡量使用C++風格的注釋
            C++的注釋可以在注釋里還有注釋,所以注釋掉一個代碼塊不用刪除這段代碼的注釋。C則不行。

            條款5:對應的new和delete要采用相同的形式
            調 用new時用了[],調用delete時也要用 []。如果調用new時沒有用[],那調用delete時也不要用[]。對于typedef來說,用new創建了一個typedef定義的類型的對象后, delete時必須根據typedef定義的類型來刪除。因此,為了避免混亂,最好杜絕數組類型用typedef。

            條款6:析構函數里對指針成員調用delete
            刪除空指針是安全的,因此在析構函數里可以簡單的delete類的指針成員,而不用擔心他們是否被new過。

            條款7:預先準備好內存不足的情況
            1.用try-cache來捕獲拋出的異常。
            2. 當內存分配請求不能滿足時,調用預先指定的一個出錯處理函數。這個方法基于一個常規,即當operator new不能滿足請求時,會在拋出異常之前調用客戶指定的一個出錯處理函數—一般稱之為new-handler函數。還可以創建一個混合風格的基類—這種基 類允許子類繼承它某一特定的功能(即函數)。

            條款8:寫operator new和operator delete時要遵循常規
            內存分配程序支持new-handler函數并正確地處理了零內存請求,并且內存釋放程序處理了空指針。此外還必須要有正確的返回值。

            條款9:避免隱藏標準形式的new
            在 類里定義了一個稱為“operator new”的函數后,會不經意地阻止了對標準new的訪問(到底如何隱藏的???)。一個辦法是在類里寫一個支持標準new調用方式的operator new,它和標準new做同樣的事,這可以用一個高效的內聯函數來封裝實現。另一種方法是為每一個增加到operator new的參數提供缺省值。

            條款10:如果寫了operator new就要同時寫operator delete
            operator new和operator delete需要同時工作,如果寫了operator new,就一定要寫operator delete。對于為大量的小對象分配內存的情況,可以考慮使用內存池,以犧牲靈活性來換取高效率。

            條款11:為需要動態分配內存的類聲明一個拷貝構造函數和一個賦值操作符
            如果沒有自定已拷貝構造函數和賦值操作符,C++會生成并調用缺省的拷貝構造函數和賦值操作符,它們對對象里的指針進行逐位拷貝,這會導致內存泄漏和指針重復刪除。因此,只要類里有指針時,就要寫自己版本的拷貝構造函數和賦值運算符函數。

            條款12:盡量使用初始化而不要在構造函數里賦值
            盡量使用成員初始化列表,一方面對于成員來說只需承擔一次拷貝構造函數的代價,而非構造函數里賦值時的一次(缺省)構造函數和一次賦值函數的代價;另一方面const和引用成員只能被初始化而不能被賦值。

            條款13:初始化列表中的成員列出的順序和它們在類中聲明的順序相同
            類的成員是按照它們在類里被聲明的順序進行初始化的,和它們在成員初始化列表中列出的順序沒有關系。

            條款14:確定基類有虛析構函數
            通過基類的指針去刪除派生類的對象,而基類有沒有虛析構函數時,結果將是不可確定的。因此必須將基類的析構函數聲明為virtual。但是,無故的聲明虛析構函數和永遠不去聲明一樣是錯誤的,聲明虛函數將影響效率。

            條款15:讓operator=返回*this的引用
            當 定義自己的賦值運算符時,必須返回賦值運算符左邊參數的引用,*this。如果不這樣做,就會導致不能連續賦值,或導致調用時的隱式類型轉換不能進行(隱 式類型轉換時要用到臨時對象,而臨時對象是const的),或兩種情況同時發生。對于沒有聲明相應參數為const的函數來說,傳遞一個const對象是 非法的。

            條款16:在operator=中對所有數據成員賦值
            當類里增加新的數據成員時,要記住更新賦值運算符函數。對基類的私有成員賦值時,可以顯示調用基類的operator=函數。派生類的拷貝構造函數中必須調用基類的拷貝構造函數而不是缺省構造函數,否則基類的數據成員將不能初始化。

            條款17:在operator=中檢查給自己賦值的情況
            顯 示的自己給自己賦值不常見,但是程序中可能存在隱式的自我賦值:一個對象的兩個不同名字(引用)互相賦值。首先,如果檢查到自己給自己賦值就立即返回,可 以節省大量的工作;其次,一個賦值運算符必須首先釋放掉一個對象的資源,然后根據新值分配新的資源,在自己給自己的情況下,釋放舊的資源將是災難性的。

            條款18:爭取使類的接口完整并且最小
            必要的函數是拷貝構造函數,賦值運算符函數,然后在此基礎上選擇必要的、方便的函數功能進行添加。

            條款19:分清成員函數,非成員函數和友元函數
            ■虛函數必須是成員函數。如果f必須是虛函數,就讓它稱為類c的成員函數。
            ■ioerator>>和operator<<決不能是成員函數。如果f是operator>>或operator<<,讓f稱為非成員函數。如果f還需要 訪問c的非公有成員,讓f稱為c的友元。
            ■其它情況下都聲明為成員函數。如果以上情況都不是,讓f稱為c的成員函數。
            Result = onehalf * 2;能通過編譯的原因:調用重載*操作符的成員函數,對參數2進行隱式類型轉換。
            Result = 2 * onehalf;不能通過編譯的原因:不能對成員函數所在對象(即成員函數中this指針指向的對象)進行轉換。

            條款20:避免public接口出現數據成員
            訪問一致性,public接口里都是函數。
            精確的訪問控制,可以精確設置數據成員的讀寫權限。
            功能分離,可以用一段計算來取代一個數據成員。舉例:計算汽車行駛的平均速度。

            條款21:盡量使用const
            如果const出現在*號左邊,指針指向的數據為常量;如果const出現在*號右邊,則指針本身為常量;如果const在兩邊都出現,二者都是常量。
            將operator的返回結果聲明為const,以防止對返回結果賦值,這樣不合常規。
            c+ +中的const:成員函數不修改對象中的任何數據成員時,即不修改對象中的任何一個比特時,這個成員函數才是const的。造成的問題是可以修改指針指 向的值,而且不能修改對象中的一些必要修改的值。解決方案是將必要修改的成員運用mutable關鍵字。另一種方法是使用const_cast初始化一個 局部變量指針,使之指向this所指的同一個對象來間接實現。還有一種有用又安全的方法:在知道參數不會在函數內部被修改的情況下,將一個const對象 傳遞到一個取非const參數的函數中。

            條款22:盡量用“傳引用”而不用“傳值”
            傳值將導致昂貴的對象開銷,而傳引用則非常高效。
            傳引用避免了“切割問題”,即當一個派生類的對象作為基類對象被傳遞是,派生類的對象的作為派生類所具有的所有行為特性會被“切割”掉,從而變成了一個簡單的基類對象。

            條款23:必須返回一個對象時不要試圖返回一個引用縮寫
            典型情況:操作符重載。
            常見的錯誤:
            返回引用,返回的是局部對象的引用。
            堆中構造,使用new分配內存,但是無人負責delete的調用,從而造成內存泄漏。
            返回靜態對象,導致調用同一函數比較時總是相等。
            正確的方法是直接在堆棧中創建對象并返回。

            條款24:在函數重載和設定參數缺省值間慎重選擇
            如果可以選擇一個合適的缺省參數,否則就使用函數重載。
            有一些情況必須使用重載:函數的結果取決于傳入參數的個數;需要完成一項特殊的任務。

            條款25:避免對指針和數字類型重載
            對于f(0):0代表int還是null。編譯器認為是int,這和人們的想象不一樣。解決辦法是使用成員模板,構造一個可以產生null指針對象的類。最重要的是,只要有可能,就要避免對一個數字和一個指針類型重載。

            條款26:當心潛在二義性
            情形1:可以通過構造函數和轉換運算符產生另一個類的對象,這時編譯器將拒絕對其中的一種方法進行選擇。
            情形2:f(int);f(char);對于f(double)時產生二義。
            情形3:多繼承時,兩個基類有同名的成員。此時必須指定基類方可調用,而不考慮訪問控制權限和返回值。

            條款27:如果不想使用隱式生成的函數就要顯式地禁止它
            方法是聲明該函數,并使之為private。顯式地聲明一個成員函數,就防止了編譯器去自動生成它的版本;使函數為private,就防止了別人去調用它。為了防止成員函數和友元函數的調用,只聲明而不定義這個函數。

            條款28:劃分全局名字空間
            使用名字空間,以防止不同庫的名字沖突。對于不支持名字空間的編譯器,可以使用struct來模擬名字空間,但是此時運算符只能通過函數調用來使用。

            條款29:避免返回內部數據的句柄
            對于const成員函數來說,返回句柄可能會破壞數據抽象。如果返回的不是指向const數據的句柄,數據可能被修改。對非const成員函數來說,返回句柄會帶來麻煩,特別是涉及到臨時對象時。句柄就象指針一樣,可以是懸浮的。

            條款30:避免這樣的成員函數:其返回值是指向成員的非const指針或引用,但成員的訪問級比這個函數要低
            如果獲得了私有或保護成員(包括成員函數)的地址(指針或引用),那么就可以象對待公有成員一樣進行訪問。如果不得不返回其引用或指針,可以通過返回指向const對象的指針或引用來達到兩全其美的效果。

            條款31:千萬不要返回局部對象的引用,也不要返回函數內部用new初始化的指針的引用
            如 果返回局部對象的引用,那個局部對象其實已經在函數調用者使用它之前就被銷毀了。而返回廢棄指針的問題是必須要有人負責調用delete,而且對于 product=one*two*three*four;的情況,將產生內存泄漏。因此,寫一個返回廢棄指針的函數無異于坐等內存泄漏的來臨。

            條款32:盡可能地推遲變量的定義
            不 僅要強變量的定義推遲到必須使用它的時候,還要盡量推遲到可以為它提供一個初始化參數位置。這樣做,不僅可以避免對不必要的對象進行構造和析構,還可以避 免無意義的對缺省構造函數的調用。而且,在對變量初始化的場合下,變量本身的用途不言自明,在這里定義變量有益于表明變量的含義。

            條款33:明智使用內聯
            內聯函數的本質是將每個函數調用以它的代碼體來替換。
            大多數編譯器拒絕“復雜”的內聯函數(例如,包含循環和遞歸的函數);還有,即使是最簡單的虛函數調用,編譯器的內聯處理程序對它也愛莫能助。
            若編譯器不進行內聯,則將內聯函數當作一般的“外聯”函數來處理。這稱為“被外聯的內聯”。
            找出重要的函數,將它內聯。同時要注意代碼膨脹帶來的問題,并監視編譯器的警告信息,看看是否有內聯函數沒有被編譯器內聯。

            條款34:將文件間的編譯依賴性降至最低
            ■如果可以使用對象的引用和指針,就要避免使用對象本身。定義某個類型的引用和指針只會涉及到這個類型的聲明,定義此類型的對象則需要類型定義的參與。
            ■盡可能使用類的聲明,而不使用類的定義。因為在聲明一個函數時,如果用到某個類,是絕對不需要這個類的定義的,即使函數是通過傳值來傳遞和返回這個類。
            ■不要在頭文件中再包含其它頭文件,除非缺少了它們就不能編譯。相反,要一個一個地聲明所需要的類,讓使用這個頭文件的用戶自己去包含其它的頭文件。
            ■最后一點,句柄類和協議類都不大會使用類聯函數。使用任何內聯函數時都要訪問實現細節,而設計句柄類和協議類的初衷正是為了避免這種情況。

            條款35:使公有繼承體現“是一個”的含義
            如果類D從類B公有繼承時,類型D的每一個對象也是類型B的一個對象,但反之不成立。任何可以使用類型B的對象的地方,類型D的對象也可以使用。
            特別注意一般理解中的“是一個”,比如企鵝是鳥,并不嚴密。如果涉及到飛這個動作,二者之間不適合使用公有繼承。

            條款36:區分接口繼承課實現繼承
            定義純虛函數的目的在于,使派生類僅僅只是繼承函數的接口。也可以為純虛函數提供一種缺省實現。
            聲明簡單虛函數的目的在于,使派生類繼承函數的接口和缺省實現。
            聲明非虛函數的目的在于,使派生類繼承函數的接口和強制性實現。

            條款37:絕不要重新定義繼承而來的非虛函數
            如果重新定義繼承而來的非虛函數,將導致對象對函數的調用結果由指向其的指針決定,而不是由對象本身的類型來決定。另外,也是類的設計產生矛盾,因為公有繼承的含義是“是一個”,改變繼承而來的方法顯然是不合理的。

            條款38:絕不要重新定義繼承而來的缺省參數值
            虛函數動態綁定,而缺省參數是靜態綁定。因此重新定義繼承而來的缺省參數值可能造成調用的是定義在派生類,但使用了基類中缺省參數值的虛函數。

            條款39:避免“向下轉換”繼承層次
            采用向下轉換時,將不利于對代碼進行維護,可以采用虛函數的方法來解決。
            不得不進行向下轉換時,采用安全的向下轉換:dynamic_cast運算符。dynamic_cast運算符先嘗試轉換,若轉換成功就返回新類型的合法指針,若失敗則返回空指針。

            條款40:通過分層來體現“有一個”或“用...來實現”
            公有繼承的含義是“是一個”。對應地,分層的含義是“有一個”或“用...來實現”。例如,要實現set類,因為list中可以包含重復元素,因此set不是一個list。set可以用list來實現,即在set中包含一個list。

            條款41:區分繼承和模板
            當對象的類型不影響類中函數的行為時,就要使用模板來生成這樣一組類。
            當對象的類型影響類中函數的行為時,就要使用繼承來得到這樣一組類。

            條款42:明智地使用私有繼承
            關于私有繼承的兩個規則:和公有繼承相反,如果兩個類之間的繼承關系為私有,編譯器一般 不會將派生類對象轉換為基類對象;從私有基類繼承而來的成員都稱為了派生類的私有成員,即使它們在基類中是保護或公有成員。
            私有繼承意味這“用...”來實現,但是應盡可能使用分層,必須時才使用私有繼承。
            條款43:明智地使用多繼承
            多 繼承后果:二義性,如果一個派生類從多個基類繼承了一個成員名,所有對這個名字的訪問都是二義的,你必須明確說出你所指的是哪個成員。這可能導致虛函數的 失效,并且不能對多繼承而來的幾個相同名稱虛函數同時進行重定義。鉆石型繼承,此時向虛基類傳遞構造函數參數時要在繼承結構中最底層派生類的成員初始化列 表中指定。同時還要仔細想想虛函數的優先度。
            然而在適當時候還是可以使用多繼承,例如將接口的公有繼承和實現的私有繼承結合起來的情況。
            以增加中間類的代價來消除多繼承有時侯是值得的。一般應該避免使用多繼承以減少繼承結構的復雜性。

            條款44:說你想說的,理解你所說的
            理解不同的面向對象構件在C++中的含義:
            · 共同的基類意味著共同的特性。
            · 公有繼承意味著 "是一個"。
            · 私有繼承意味著 "用...來實現"。
            · 分層意味著 "有一個" 或 "用...來實現"。
            下面的對應關系只適用于公有繼承的情況:
            · 純虛函數意味著僅僅繼承函數的接口。
            · 簡單虛函數意味著繼承函數的接口加上一個缺省實現。
            · 非虛函數意味著繼承函數的接口加上一個強制實現。

            條款45:弄清C++在幕后為你所寫、所調用的函數
            如 果沒有聲明下列函數,編譯器會聲明它自己的版本:一個拷貝構造函數,一個賦值運算符,一個析構函數,一對取址運算符和一個缺省構造函數。對于拷貝構造函數 和賦值運算符,官方的規則是:缺省拷貝構造函數(賦值運算符)對類的非靜態數據成員進行“以成員為單位的”逐一拷貝構造(賦值)。
            特別要注意由于編譯器自動生成的函數造成的編譯錯誤。

            條款46:寧可編譯和鏈接是出錯,也不要運行時出錯
            通常,對設計做一點小小的改動,就可以在編譯期間消除可能產生的運行時錯誤。這常常涉及到在程序中增加新的數據類型。例如對于需要類型檢查的Month,可以將其設為一個Month類:構造函數私有,產生對象使用靜態成員函數,每個Month對象為const。

            條款47:確保非局部靜態對象在使用前被初始化
            如果在某個被編譯單元中,一個對象的初始化要依賴于另一個被編譯單元中的另一個對象的值,并且這第二個對象本身也需要初始化,就有可能造成混亂。
            雖 然關于 "非局部" 靜態對象什么時候被初始化,C++幾乎沒有做過說明;但對于函數中的靜態對象(即,"局部" 靜態對象)什么時候被初始化,C++卻明確指出:它們在函數調用過程中初次碰到對象的定義時被初始化。如果不對非局部靜態對象直接訪問,而用返回局部靜態 對象引用的函數調用來代替,就能保證從函數得到的引用指向的是被初始化了的對象。

            條款48:重視編譯器警告
            重視編譯器產生的每一條警告信息。在忽略一個警告之前,一定要準確理解它想告訴你的含義。

            條款49:熟悉標準庫
            對 于C++頭文件,最大的挑戰是把字符串頭文件理清楚:是舊的C頭文件,對應的是基于char*的字符串處理函數; 是包裝了std的C++頭文件,對應的是新的string類(看下文);是對應于舊C頭文件 的std版本。如果能掌握這些,其余的也就容易了。
            庫中的一切都是模板。

            條款50:提高對C++的認識
            C++的設計目標:和C兼容,效率,和傳統開發工具及環境的兼容性,解決真實問題的可應用性。
            參考C++標準,理解C++的設計過程。
            文章來源:http://my.donews.com/robinchow/2007/01/10/ofzrlddyftbhlvscqmkjicnypymamyhaehrq/

            posted on 2007-10-23 21:01 Robin Chow 閱讀(152) 評論(0)  編輯 收藏 引用

            導航

            統計

            常用鏈接

            留言簿(1)

            隨筆分類

            隨筆檔案

            搜索

            最新評論

            閱讀排行榜

            評論排行榜

            亚洲精品乱码久久久久久| 国产综合免费精品久久久| av色综合久久天堂av色综合在| 久久天天躁狠狠躁夜夜2020一| 乱亲女H秽乱长久久久| 国产视频久久| 一本色道久久综合亚洲精品| 久久精品国产亚洲欧美| 天天综合久久一二三区| 77777亚洲午夜久久多喷| 日日狠狠久久偷偷色综合0 | 国产三级久久久精品麻豆三级| 久久er热视频在这里精品| 午夜精品久久久久久影视riav| 久久久久久亚洲精品成人| 老司机午夜网站国内精品久久久久久久久| 一本色道久久88—综合亚洲精品| 国产精品一区二区久久精品无码 | 国产三级观看久久| 精品人妻伦九区久久AAA片69| 97超级碰碰碰久久久久| 一本色道久久88—综合亚洲精品| 99久久精品国产一区二区三区| 伊人久久大香线蕉综合5g| 狠色狠色狠狠色综合久久| 伊人久久综合无码成人网| 久久久人妻精品无码一区| 欧美亚洲另类久久综合| 久久久久亚洲AV无码网站| 精产国品久久一二三产区区别| 久久久久黑人强伦姧人妻| 亚洲天堂久久精品| 18岁日韩内射颜射午夜久久成人| 无码专区久久综合久中文字幕| 精品久久久无码人妻中文字幕| 久久久久高潮综合影院| 免费久久人人爽人人爽av| 亚洲午夜久久久久久噜噜噜| 国产aⅴ激情无码久久| 亚洲精品乱码久久久久久自慰| 久久天天躁夜夜躁狠狠|