• <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:盡量使用初始化而不要在構造函數里賦值
            盡量使用成員初始化列表,一方面對于成員來說只需承擔一次拷貝構造函數的代價,而非構造函數里賦值時的一次(缺?。嬙旌瘮岛鸵淮钨x值函數的代價;另一方面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 @ 2007-10-23 21:01 Robin Chow 閱讀(152) | 評論 (0)編輯 收藏

            [導入]Exception C++ 之 6

            如果對||予以正確恰當的重載,那么在 if 語句中,||或許是一個函數調用。如果 其是一個函數調用,那么“短路求值規則”會被抑住,這樣 if 語句中的所有條件將總是被求值。
            例如:
            if(e.Title() == "CEO" || e.Salary() > 10000) cout << "hello" << endl;
            如果對==、||和>進行適當重載,則兩個表達式都將被求值。

            文章來源:http://my.donews.com/robinchow/2007/01/10/wtFbMixKLhXCHOcnoKJheNEPFczsPCQmVvoI/

            posted @ 2007-10-23 21:01 Robin Chow 閱讀(179) | 評論 (0)編輯 收藏

            [導入]我為什么不想成為 VC 程序員

            今天在論壇上看到這樣一段話,感觸頗深:
            在我的早期職業生涯中,也曾是個VC程序員,也曾以為駕馭了VC就是駕馭了自己的技術生涯。但漸漸地,我發現無論我潛心研究多深,付出多少精力,在大多數
            問題上,我無法給自己滿意的答案。似乎自己在金字塔大大小小、寬寬窄窄的隧道里躑躅前行,但每走了一大段路后,你總能碰到一扇門,一扇單靠自己很難逾越的
            門。后來發現,很多VC程序員也有同感:在做過若干年后,總會感覺面前立起一個檻,沒辦法逾越。這不是平臺期之類的階段,平臺期之后通常會有一個大的提升
            階段,但在那扇門前,很少有人窺得到門后的景觀。沒錯,很多人成熟起來后明白了這扇門來自何方,為甚所立,所擋為何,這扇門就是:Microsoft。
            作為一個盈利組織,微軟設立層層技術壁壘,使自己的產品立于不敗之地,爭取盈利最大化,為大大小小的投資人負責,這無可厚非。但作為一個靠技術生存的人,
            除非你進入微軟視窗系統開發部門,否則很難打開那扇門。所以,大部分的VC程序員,無論付出多少,最終淪為平庸,也就不奇怪了:你只能知道這些,這是游戲
            規則的一條,只是你自己沒仔細看清楚而已。
            像大多數理想主義者在發現自己最終是在為最不理想主義的手牽著鼻子走之后,很多VC程序員開始淪為最現實的技術人員,自甘平庸,寧愿囚在windows囚
            室中,但只要擁有一份過得去的薪水,一個技術高手的光環,就像《matrix》里描述的一類人,寧愿生活在虛幻中,但只要過得體面,那也成。在這間囚室
            中,我發現自己只是在復制別人的工作,甚至都無法理解別人是怎么做的。我根本不會設計軟件!
            但也有一部分更為堅定的理想主義者,不但沒有淪喪,反而升華,研究VC,只是他們追求烏托邦理想鏈條中的一環而已,在進行充分研究后,他們繼續前行,只是
            少了更多的羈伴。他們中的很多人甚至放棄了windows平臺,開始寬泛地、深入地理解計算機科學的精髓及其外圍。既然要透徹地領悟一樣知識,那就不該有
            不可逾越的門。自然而然地,這些真正的理想主義者開始投入開放源代碼世界。在這個世界里,只要你愿意,你可以打開任何一扇門,貪婪地汲取任何你需要的養
            料,你所需要接受的游戲規格卻非常簡單:與別人無償地分享你的知識,并把這個游戲規則教給別人。在這個開放的世界里,不再有壁壘,你會明確地知道自己在干
            什么,只要你愿意。因而,你也能夠做出一些真正original的工作。這不正是在那個午后,填報志愿時,我們曾立志奉獻一生的理想嗎?
            最后,送出一句忠告:不要固步自封,盡量放open;如果做不到的話,那就盡量承認自己盡管不過20歲,卻已經老去了罷。

            文章來源:http://my.donews.com/robinchow/2007/01/11/pjhceeuybqztyteeaggsrqunorchjhyzqgcv/

            posted @ 2007-10-23 21:01 Robin Chow 閱讀(308) | 評論 (0)編輯 收藏

            [導入]編寫異常安全的代碼

            對于異常安全的評定,可分為三個級別:基本保證、強保證和不會失敗。

              基本保證:確保出現異常時程序(對象)處于未知但有效的狀態。所謂有效,即對象的不變式檢查全部通過。
              強保證:確保操作的事務性,要么成功,程序處于目標狀態,要么不發生改變。
              不會失敗:對于大多數函數來說,這是很難保證的。對于C++程序,至少析構函數、釋放函數和swap函數要確保不會失敗,這是編寫異常安全代碼的基礎。

            總結一下編寫異常安全代碼的幾條準則:

              1.只在應該使用異常的地方拋出異常
              2.如果不知道如何處理異常,請不要捕獲(截留)異常。
              3.充分使用RAII(smart ptr),旁路異常。
              4.努力實現強保證,至少實現基本保證。
              5.確保析構函數、釋放類函數和swap不會失敗。

              另外,還有一些語言細節問題:

              1.不要這樣拋出異常:throw new exception;這將導致內存泄漏。
              2.自定義類型,應該捕獲異常的引用類型:catch(exception& e)或catch(const exception& e)。
              3.不要使用異常規范,即使是空異常規范。編譯器并不保證只拋出異常規范允許的異常,更多內容請參考相關書籍。


            文章來源:http://my.donews.com/robinchow/2007/01/11/vdcfhpltuhbzmtzupiknflljcnxqpkyloaqv/

            posted @ 2007-10-23 20:51 Robin Chow 閱讀(122) | 評論 (0)編輯 收藏

            [導入]Exception C++ 之 7

            關于強異常安全性:
            1. 要對強異常安全性提供保證,經常需要以放棄一部分性能為代價。
            2. 如果一個函數含有多重的副作用,那么其總是無法稱為強異常安全的。此時,惟一的方法就是將函數分為幾個函數,以使得每一個分出來的函數之副作用能被自動完成。
            3. 并不是所有函數都需要具有強異常安全性。

            文章來源:http://my.donews.com/robinchow/2007/01/11/ulvagfkakeqfccpqydkwfkcfgfqopwvjiqkp/

            posted @ 2007-10-23 20:51 Robin Chow 閱讀(66) | 評論 (0)編輯 收藏

            [導入]Exception C++ 之 8

            類的設計準則:
            1. 盡量重復運用代碼,特別是標準庫。這樣不但比較快,也比較容易,比較安全。
            2. 小心隱式轉換所帶來的隱式臨時對象。避免這東西的一個好辦法就是盡可能讓 constructors 稱為 explicit,并且避免寫出轉換運算子。
            3. 盡量以 by const&(而非 by value)的方式來傳遞物件。
            4. 盡量寫“a op = b;”而不要寫成“a = a op b;”(其中 op 代表任何運算子)。這樣不但比較清楚,通常也比較有效率。
            5. 如果提供了某個運算子的標準版(例如 operator+),同時也應該為它提供一份 assignment 版(例如 operator+=)并且以后者為基礎來實現前者。同時總是保存 op 和 op= 之間的自然關系(其中 op 代表任何運算子)。
            6. 使用一下準則來決定一個運算子應該是 member function 或應該是個 nonmember function:
              • 一元運算子應該是 members。
              • = () [] 和 -> 必須是members。
              • assignment 版的運算子(+= -= /= *= 等等)都必須是members。
              • 其他所有二元運算子都應該是 nonmembers。
            7. 總是在 operator<< 和 operator>> 函數中傳回 stream references。
            8. 為了一致性,應該總是以前置式累加運算符為本,實現出后置式累加運算符。
            9. 不要在變量名前面加上下劃線,因為標準規個書中保留了某些“下劃線”開頭的識別符號給編譯器使用。

            文章來源:http://my.donews.com/robinchow/2007/01/11/jabxiufnsuhdoddxaufbdnewyegsorjxrkua/

            posted @ 2007-10-23 20:51 Robin Chow 閱讀(88) | 評論 (0)編輯 收藏

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

            條款1:指針與引用的區別
            二者之間的區別是:在任何情況下都不能用指向空值的引用,而指針則可以;指針可以被重新賦值以指向另一個不同的對象,但是引用則總是指向在初始化時被指定的對象,以后不能改變。
            在以下情況下使用指針:一是存在不指向任何對象的可能性;二是需要能夠在不同的時刻指向不同的對象。
            在以下情況使用引用:總是指向一個對象且一旦指向一個對象之后就不會改變指向;重載某個操作符時,使用指針會造成語義誤解。

            條款2:盡量使用C++風格的類型轉換
            static_cast:功能上基本上與C風格的類型轉換一樣強大,含義也一樣。但是不能把struct轉換成int類型或者把double類型轉換成指針類型。另外,它不能從表達式中去除const屬性。
            const_cast:用于類型轉換掉表達式的const或volatileness屬性。但是不能用它來完成修改這兩個屬性之外的事情。
            dynamic_cast:用于安全地沿著類的繼承關系向下類型轉換。失敗的轉換將返回空指針或者拋出異常。
            reinterpret_cast:這個操作符被用于的類型轉換的轉換結果時實現時定義。因此,使用它的代碼很難移植。最普通的用途就是在函數指針之間進行轉換。

            條款3:不要使用多態性數組
            多態和指針算法不能混合在一起使用,所以數組和多態也不能用在一起。
            數組中各元素的內存地址是數組的起始地址加上之前各個元素的大小得到的,如果各元素大小不一,那么編譯器將不能正確地定位元素,從而產生錯誤。

            條款4:避免無用的缺省構造函數
            沒有缺省構造函數造成的問題:通常不可能建立對象數組,對于使用非堆數組,可以在定義時提供必要的參數。另一種方法是使用指針數組,但是必須刪除數組里的每個指針指向的對象,而且還增加了內存分配量。
            提供無意義的缺省構造函數會影響類的工作效率,成員函數必須測試所有的部分是否都被正確的初始化。

            條款5:謹慎定義類型轉換函數
            缺省的隱式轉換將帶來出乎意料的結果,因此應該盡量消除,使用顯式轉換函數。通過不聲明運算符的方法,可以克服隱式類型轉換運算符的缺點,通過使用explicit關鍵字和代理類的方法可以消除單參數構造函數造成的隱式轉換。

            條款6:自增和自減操作符前綴形式與后綴形式的區別
            后綴式有一個int類型參數,當函數被調用時,編譯器傳遞一個0作為int參數的值傳遞給該函數。可以在定義時省略掉不想使用的參數名稱,以避免警告信息。
            后綴式返回const對象,原因是 :使該類的行為和int一致,而int不允許連續兩次自增后綴運算;連續兩次運算實際只增一次,和直覺不符。
            前綴比后綴效率更高,因為后綴要返回對象,而前綴只返回引用。另外,可以用前綴來實現后綴,以方便維護。

            條款7:不要重載&&,||,或者“,”
            對 于以上操作符來說,計算的順序是從左到右,返回最右邊表達式的值。如果重載的話,不能保證其計算順序和基本類型想同。操作符重載的目的是使程序更容易閱 讀,書寫和理解,而不是來迷惑其他人。如果沒有一個好理由重載操作符,就不要重載。而對于&&,||和“,”,很難找到一個好理由。

            條款8:理解各種不同含義的new和delete
            new操作符完成的功能分兩部分:第一部分是分配足夠的內存以便容納所需類型的對象;第二部分是它調用構造函數初始化內存中的對象。new操作符總是做這兩件事,我們不能以任何方式改變它的行為。
            我們能改變的是如何為對象分配內存。new操作符通過調用operator new來完成必需的內存分配,可以重寫或重載這個函數來改變它的行為??梢燥@式調用operator來分配原始內存。
            如果已經分配了內存,需要以此內存來構造對象,可以使用placement new,其調用形式為new(void* buffer)class(int size)。
            對于delete來說,應該和new保持一致,怎樣分配內存,就應該采用相應的辦法釋放內存。
            operator new[]與operator delete[]和new與delete相類似。

            條款9:使用析構函數防止資源泄漏
            使用指針時,如果在delete指針之前產生異常,將會導致不能刪除指針,從而產生資源泄漏。
            解決辦法:使用對象封裝資源,如使用auto_ptr,使得資源能夠自動被釋放。

            條款10:在構造函數中防止資源泄漏
            類中存在指針時,在構造函數中需要考慮出現異常的情況:異常將導致以前初始化的其它指針成員不能刪除,從而產生資源泄漏。解決辦法是在構造函數中考慮異常處理,產生異常時釋放已分配的資源。最好的方法是使用對象封裝資源。

            條款11:禁止異常信息傳遞到析構函數外
            禁止異常傳遞到析構函數外的兩個原因:第一能夠在異常傳遞的堆棧輾轉開解的過程中,防止terminate被調用;第二它能幫助確保析構函數總能完成我們希望它做的所有事情。
            解決方法是在析構函數中使用try-catch塊屏蔽所有異常。

            條款12:理解“拋出一個異?!迸c“傳遞一個參數”或“調用一個虛函數”間的差異
            有 三個主要區別:第一,異常對象在傳遞時總被進行拷貝。當通過傳值方式捕獲時,異常對象被拷貝了兩次。對象作為參數傳遞給函數時不需要被拷貝;第二,對象作 為異常被拋出與作為參數傳遞給函數相比,前者類型轉換比后者少(前者只有兩種轉換形式:繼承類與基類的轉換,類型化指針到無類型指針的轉換);最后一點, catch子句進行異常類型匹配的順序是它們在源代碼中出現的順序,第一個類型匹配成功的擦他處將被用來執行。當一個對象調用一個虛函數時,被選擇的函數 位于與對象類型匹配最佳的類里,急事該類不是在源代碼的最前頭。

            條款13:通過引用捕獲異常
            有三個選擇可以捕獲異常:第一、指 針,建立在堆中的對象必需刪除,而對于不是建立在堆中的對象,刪除它會造成不可預測的后果,因此將面臨一個難題:對象建立在堆中還是不在堆中;第二、傳 值,異常對象被拋出時系統將對異常對象拷貝兩次,而且它會產生“對象切割”,即派生類的異常對象被作為基類異常對象捕獲時,它的派生類行為就被切割調了。 這樣產生的對象實際上是基類對象;第三、引用,完美解決以上問題。

            條款14:審慎使用異常規格
            避免調用unexpected函數 的辦法:第一、避免在帶有類型參數的模板內使用異常規格。因為我們沒有辦法知道某種模板類型參數拋出什么樣的異常,所以不可能為一個模板提供一個有意義的 異常規格;第二、如果在一個函數內調用其它沒有異常規格的函數時應該去除這個函數的異常規格;第三、處理系統本身拋出的異常??梢詫⑺械?unexpected異常都被替換為自定義的異常對象,或者替換unexpected函數,使其重新拋出當前異常,這樣異常將被替換為 bad_exception,從而代替原來的異常繼續傳遞。
            很容易寫出違反異常規格的代碼,所以應該審慎使用異常規格。

            條款15:了解異常處理的系統開銷
            三 個方面:第一、需要空間建立數據結構來跟蹤對象是否被完全構造,還需要系統時間保持這些數據結構不斷更新;第二、try塊。無論何時使用它,都得為此付出 代價。編譯器為異常規格生成的代碼與它們為try塊生成的代碼一樣多,所以一個異常規格一般花掉與try塊一樣多的系統開銷。第三、拋出異常的開銷。因為 異常很少見,所以這樣的事件不會對整個程序的性能造成太大的影響。

            條款16:牢記80─20準則
            80─20準則說的是大約20%的代碼使用了80%的程序資源,即軟件整體的性能取決于代碼組成中的一小部分。使用profiler來確定程序中的那20%,關注那些局部效率能夠被極大提高的地方。

            條款17:考慮使用懶惰計算法
            懶惰計算法的含義是拖延計算的時間,等到需要時才進行計算。其作用為:能避免不需要的對象拷貝,通過使用operator[]區分出讀寫操作,避免不需要的數據庫讀取操作,避免不需要的數字操作。但是,如果計算都是重要的,懶惰計算法可能會減慢速度并增加內存的使用。

            條款18:分期攤還期望的計算
            核心是使用過度熱情算法,有兩種方法:緩存那些已經被計算出來而以后還有可能需要的值;預提取,做比當前需要做的更多事情。
            當必須支持某些操作而不總需要其結果時,可以使用懶惰計算法提高程序運行效率;當必須支持某些操作而其結果幾乎總是被需要或不止一次地需要時,可以使用過度熱情算法提高程序運行效率。

            條款19:理解臨時對象的來源
            臨時對象產生的兩種條件:為了是函數成功調用而進行隱式類型轉換和函數返回對象時。
            臨時對象是有開銷的,因此要盡可能去消除它們,然而更重要的是訓練自己尋找可能建立臨時對象的地方。在任何時候只要見到常量引用參數,就存在建立臨時對象而綁定在參數上的可能性。在任何時候只要見到函數返回對象,就會有一個臨時對象被建立(以后被釋放)。

            條款20:協助完成返回值優化
            應當返回一個對象時不要試圖返回一個指針或引用。
            C+ +規則允許編譯器優化不出現的臨時對象,所有最佳的辦法莫過于:retrun Ratinal(lhs.numerator()*rhs.numerator(), lhs.denominator()*rhs.denominator())。這種優化是通過使用函數的retuan location(或者用在一個函數調用位置的對象來替代),來消除局部臨時對象,這種優化還有一個名字:返回值優化。

            條款21:通過重載避免隱式類型轉換
            隱式類型轉換將產生臨時對象,從而帶來額外的系統開銷。
            解決辦法是使用重載,以避免隱式類型轉換。要注意的一點是在C++中有一條規則是每一個重載的operator必須帶有一個用戶定義類型的參數(這條規定是有道理的,如果沒有的話,程序員將能改變預定義的操作,這樣做肯定吧程序引入混亂的境地)。
            另外,牢記80─20規則,沒有必要實現大量的重載函數,除非有理由確信程序使用重載函數后整體效率會有顯著提高。

            條款22:考慮用運算符的賦值形式取代其單獨形式
            運算符的賦值形式不需要產生臨時對象,因此應該盡量使用。對運算符的單獨形式的最佳實現方法是return Rational(lhs) += rhs;這種方法將返回值優化和運算符的賦值形式結合起來,即高效,又方便。

            條款23:考慮變更程序庫
            程序庫必須在效率和功能等各個方面有各自的權衡,因此在具體實現時應該考慮利用程序庫的優點。例如程序存在I/O瓶頸,就可以考慮用stdio替代iostream。

            條款24:理解虛擬函數、多繼承、虛基類和RTTI所需的代價
            虛函數所需的代價:必須為每個包含虛函數的類的virtual table留出空間;每個包含虛函數的類的對象里,必須為額外的指針付出代價;實際上放棄了使用內聯函數。
            多繼承時,在單個對象里有多個vptr(一個基類對應一個)。它和虛基類一樣,會增加對象體積的大小。
            RTTI能讓我們在運行時找到對象和類的有關信息,所以肯定有某個地方存儲了這些信息,讓我們查詢。這些信息被存儲在類型為type_info的對象里,可以通過typeid操作符訪問到一個類的typeid對象。通常,RTTI被設計為在類的vbtl上實現。

            條款25:將構造函數和非成員函數虛擬化
            構 造函數的虛擬化看似無意義,但是在實際當中有一定的用處。例如,在類中構建一個虛擬函數,其功能僅僅是實現構造函數,就可以對外界提供一組派生類的公共構 造接口。虛擬拷貝構造函數也是可以實現的,但是要利用到最近才被采納的較寬松的虛擬函數返回值類型規則。被派生類重定義的虛擬函數不用必須與基類的虛擬函 數具有一樣的返回類型。
            具有虛擬行為的非成員函數很簡單。首先編寫一個虛擬函數完成工作,然后再寫衣一個非虛擬函數,它什么也不做只是調用這個函數,可以使用內聯來避免函數調用的開銷。

            條款26:限制某個類所能產生的對象數量
            只 有一個對象:使用單一模式,將類的構造函數聲明為private,再聲明一個靜態函數,該函數中有一個類的靜態對象。不將該靜態對象放在類中原因是放在函 數中時,執行函數時才建立對象,并且對象初始化時間確定的,即第一次執行該函數時。另外,該函數不能聲明為內聯,如果內聯可能造成程序的靜態對象拷貝超過 一個。
            限制對象個數:建立一個基類,構造函數中計數加一,若超過最大值則拋出異常;析構函數中計數減一。
            編程點滴:
            ●將模板類的定義和實現放在一個文件中,否則將造成引用未定義錯誤(血的教訓);
            ●靜態數據成員需要先聲明再初始化;
            ●用常量值作初始化的有序類型的const靜態數據成員是一個常量表達式(可以作為數組定義的維數);
            ●構造函數中拋出異常,將導致靜態數組成員重新初始化。

            條款27:要求或禁止在堆中產生對象
            在堆中的對象不一定是用new分配的對象,例如成員對象,雖然不是用new分配的但是仍然在堆中。
            要 求在堆中建立對象可以將析構函數聲明未private,再建立一個虛擬析構函數進行對象析構。此時如果建立非堆對象將導致析構函數不能通過編譯。當然也可 以將構造函數聲明為private,但是這樣將導致必須聲明n個構造函數(缺省,拷貝等等)。為了解決繼承問題,可以將其聲明為protected,解決 包容問題則只能將其聲明為指針。
            沒有辦法不能判斷一個對象是否在堆中,但是可以判斷一個對象是否可以安全用delete刪除,只需在operator new中將其指針加入一個列表,然后根據此列表進行判斷。
            把一個指針dynamic_cast成void*類型(或const void*或volatile void*等),生成的指針將指向“原指針指向對象內存”的開始處。但是dynamic_cast只能用于“指向至少具有一個虛擬函數的對象”的指針上。
            禁止建立堆對象可以簡單的將operator new聲明為private,但是仍然不能判斷其是否在堆中。

            條款28:靈巧(smart)指針
            靈巧指針的用處是可以對操作進行封裝,同一用戶接口。
            靈巧指針從模板生成,因為要與內建指針類似,必須是強類型的;模板參數確定指向對象的類型。
            靈巧指針的拷貝和賦值,采取的方案是“當auto_ptr被拷貝和賦值時,對象所有權隨之被傳遞”。此時,通過傳值方式傳遞靈巧指針對象將導致不確定的后果,應該使用引用。
            記住當返回類型是基類而返回對象實際上派生類對象時,不能傳遞對象,應該傳遞引用或指針,否則將產生對象切割。
            測試靈巧指針是否為NULL有兩種方案:一種是使用類型轉換,將其轉換為void*,但是這樣將導致類型不安全,因為不同類型的靈巧指針之間將能夠互相比較;另一種是重載operator!,這種方案只能使用!ptr這種方式檢測。
            最好不要提供轉換到內建指針的隱式類型轉換操作符,直接提供內建指針將破壞靈巧指針的“靈巧”特性。
            靈巧指針的繼承類到基類的類型轉換的一個最佳解決方案是使用模板成員函數,這將使得內建指針所有可以轉換的類型也可以在靈巧指針中進行轉換。但是對于間接繼承的情況,必須用dynamic_cast指定其要轉換的類型是直接基類還是間接基類。
            為了實現const靈巧指針,可以新建一個類,該類從非const靈巧指針繼承。這樣的化,const靈巧指針能做的,非const靈巧指針也能做,從而與標準形式相同。

            條款29:引用計數
            使用引用計數后,對象自己擁有自己,當沒有人再使用它時,它自己自動銷毀自己。因此,引用計數是個簡單的垃圾回收體系。
            在基類中調用delete this將導致派生類的對象被銷毀。
            寫時拷貝:與其它對象共享一個值直到寫操作時才擁有自己的拷貝。它是Lazy原則的特例。
            精彩的類層次結構:

            RCObject類提供計數操作;StringValue包含指向數據的指針并繼承RCObject的計數操作;RCPtr是一個靈巧指針,封裝了本屬于String的一些計數操作。

            條款30:代理類
            可以用兩個類來實現二維數組:Array1D是一個一維數組,而Array2D則是一個Array1D的一維數組。Array1D的實例扮演的是一個在概念上不存在的一維數組,它是一個代理類。
            代 理類最神奇的功能是區分通過operator[]進行的是讀操作還是寫操作,它的思想是對于operator[]操作,返回的不是真正的對象,而是一個 proxy類,這個代理類記錄了對象的信息,將它作為賦值操作的目標時,proxy類扮演的是左值,用其它方式使用它,proxy類扮演的是右值。用賦值 操作符來實現左值操作,用隱式類型轉換來實現右值操作。
            用proxy類區分operator[]作左值還是右值的局限性:要實現proxy類和原類型的無縫替代,必須申明原類型的一整套操作符;另外,使用proxy類還有隱式類型轉換的所有缺點。
            編程點滴:不能將臨時對象綁定為非const的引用的行參。

            條款31:讓函數根據一個以上的對象來決定怎么虛擬
            有 三種方式:用虛函數加RTTI,在派生類的重載虛函數中使用if-else對傳進的不同類型參數執行不同的操作,這樣做幾乎放棄了封裝,每增加一個新的類 型時,必須更新每一個基于RTTI的if-else鏈以處理這個新的類型,因此程序本質上是沒有可維護性的;只使用虛函數,通過幾次單獨的虛函數調用,第 一次決定第一個對象的動態類型,第二次決定第二個對象動態類型,如此這般。然而,這種方法的缺陷仍然是:每個類必須知道它的所有同胞類,增加新類時,所有 代碼必須更新;模擬虛函數表,在類外建立一張模擬虛函數表,該表是類型和函數指針的映射,加入新類型是不須改動其它類代碼,只需在類外增加一個處理函數即 可。

            條款32:在未來時態開發程序
            未來時態的考慮只是簡單地增加了一些額外約束:
            ●提供完備的類,即使某些部分現在還沒有被使用。
            ●將接口設計得便于常見操作并防止常見錯誤。使得類容易正確使用而不易用錯。
            ●如果沒有限制不能通用化代碼,那么通用化它。

            條款33:將非尾端類設計為抽象類
            如果有一個實體類公有繼承自另一個實體類,應該將兩個類的繼承層次改為三個類的繼承層次,通過創造一個新的抽象類并將其它兩個實體類都從它繼承。因此,設計類層次的一般規則是:非尾端類應該是抽象類。在處理外來的類庫,可能不得不違反這個規則。
            編程點滴:抽象類的派生類不能是抽象類;實現純虛函數一般不常見,但對純虛析構函數,它必須實現。

            條款34:如何在同一程序中混合使用C++和C
            混合編程的指導原則:
            ●確保C++和C編譯器產生兼容的obj文件。
            ●將在兩種語言下都使用的函數申明為extern ‘C’。
            ●只要可能,用C++寫main()。
            ●總用delete釋放new分配的內存;總用free釋放malloc分配的內存。
            ●將在兩種語言間傳遞的東西限制在用C編譯的數據結構的范圍內;這些結構的C++版本可以包含非虛成員函數。

            條款35:讓自己習慣使用標準C++語言
            STL 基于三個基本概念:包容器(container)、選擇子(iterator)和算法(algorithms)。包容器是被包容的對象的封裝;選擇子是類 指針的對象,讓你能如同使用指針操作內建類型的數組一樣操作STL的包容器;算法是對包容器進行處理的函數,并使用選擇子來實現。
            文章來源:http://my.donews.com/robinchow/2007/01/11/qimyhmnmnudhngtapaqrnsghpptjuutkcfjj/

            posted @ 2007-10-23 20:51 Robin Chow 閱讀(182) | 評論 (0)編輯 收藏

            [導入]虛擬函數和重載

            如下面的程序:
            #include < iostream > using namespace std;class base{ public: virtual void f(char i){ cout << "Base class: " << i << endl; }; virtual void f(double d){ cout << "Base class: " << d << endl; } }; class derived : public base{ public: void f(char i){ cout << "Derived class: " << i << endl; } }; int main(int argc, char** argv) { base *pb = new derived; pb->f(2.0); delete pb; return 0; }
            開始的時候感覺有點迷惑,被調用的是 base 的 f 呢?還是 derived 的 f(需要進行參數類型轉換)。實驗證明,被調用的是 base 的 f。 因為重載是根據靜態類型來選擇函數的,亦即由 pb 本身的類型來決定。而虛擬則是根據動態類型來選擇函數,由 pb 指向的對象類型決定。因此編譯器首先根據重載來選擇一個基類的函數,如果該函數為虛擬函數且被派生類實現,則再通過多態機制調用派生類函數。
            文章來源:http://my.donews.com/robinchow/2007/01/11/ocipmucddsvoutkpnwjgeqrlwadgilgxnoma/

            posted @ 2007-10-23 20:51 Robin Chow 閱讀(84) | 評論 (0)編輯 收藏

            [導入]Exception C++ 之 9

            虛擬函數的設計準則:
            1. 讓 base class 的析構函數成為 virtual(除非確定不會有人企圖透過 pointer-to-base 去刪除一個 derived object)。
            2. 如果要提供一個函數,其名稱與繼承而來的函數同名時,如果不想因此隱藏了繼承而來的函數,可使用 using declaration 來顯式聲明。
            3. 絕不要在改寫虛擬函數的過程中改變預設參數。

            文章來源:http://my.donews.com/robinchow/2007/01/11/eqhivxoubayqpkaluyekyuwxtdssqhxvxfes/

            posted @ 2007-10-23 20:51 Robin Chow 閱讀(73) | 評論 (0)編輯 收藏

            [導入]Exception C++ 之 10

            在進行類層次設計時,常見的錯誤是誤用公有繼承。除非要表達的是真正的 IS-A 和 WORKS-LIKE-A 的關系,才使用公有繼承。
            設計準則:
            1. 絕對不要以公有繼承來復用基類的代碼;公有繼承是為了被復用 - 被那些“以多態方式運用基類對象”的代碼復用。
            2. 當我們需要表達“用來實現”的關系時,選擇成員方式而不要使用繼承。只有在絕對必要的情況下才使用私有繼承 - 也就是說當需要存取保護成員或是需要改寫虛擬函數時。絕對不要只為了重復運用代碼而使用公有繼承。


            文章來源:http://my.donews.com/robinchow/2007/01/13/qzpjchcewcpuedbnfmewwwychrghoqnfpdor/

            posted @ 2007-10-23 20:51 Robin Chow 閱讀(91) | 評論 (0)編輯 收藏

            僅列出標題
            共9頁: 1 2 3 4 5 6 7 8 9 

            導航

            統計

            常用鏈接

            留言簿(1)

            隨筆分類

            隨筆檔案

            搜索

            最新評論

            閱讀排行榜

            評論排行榜

            国产综合久久久久久鬼色| 91精品观看91久久久久久| 久久久无码一区二区三区| 国产日产久久高清欧美一区| 人人狠狠综合久久88成人| 久久99热这里只有精品国产| 久久综合精品国产一区二区三区| 国产精品日韩深夜福利久久| 一级做a爰片久久毛片看看| 久久综合给合久久狠狠狠97色 | 久久九色综合九色99伊人| 久久久久久亚洲精品无码| 精品久久久中文字幕人妻| 久久人人爽人人人人爽AV | 亚洲精品乱码久久久久久按摩| 看久久久久久a级毛片| 国产毛片久久久久久国产毛片| 亚洲日韩中文无码久久| 国内精品久久久久久久coent| 色婷婷综合久久久久中文| 久久国产成人午夜aⅴ影院| 久久精品无码一区二区三区日韩| 精品久久人人爽天天玩人人妻 | 丁香五月综合久久激情| 久久久久久精品无码人妻| 国产三级精品久久| 韩国三级大全久久网站| 人妻无码αv中文字幕久久| 伊人久久亚洲综合影院| 精品久久久久久无码中文野结衣 | 亚洲国产香蕉人人爽成AV片久久| 久久国产精品99精品国产987| 久久久无码精品亚洲日韩蜜臀浪潮| 伊人 久久 精品| 久久综合九色综合网站| 亚洲人AV永久一区二区三区久久| 久久久久国产精品嫩草影院| 久久精品国产一区二区三区| 久久无码一区二区三区少妇 | 无码任你躁久久久久久| 久久久久亚洲爆乳少妇无|