• <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>

            Effective C++筆記(轉(zhuǎn))

            0. 拷貝構(gòu)造函數(shù)和賦值運算符

            copy構(gòu)造函數(shù)用來“以同型對象初始化自我對象”,copy assignment操作符被用來“從另一個同型對象中”拷貝其值到自我對象

            copy構(gòu)造函數(shù)使用時,自我對象并沒有被實例化;而copy assignment操作符使用時自我對象已經(jīng)被實例化

            如:

            String str1("Hello");

            String str2(str1);    
            // copy constructor

            String str3 
            = str1;   // 注意:copy constructor !

            String str4;           

            str4 
            = str1;            // copy assignment

            1. 將構(gòu)造函數(shù)聲明為explicit可以阻止它們被用來執(zhí)行隱式類型轉(zhuǎn)換。

            如:

            有一個函數(shù) void doSomething(B bObject);

             B有一個接受int的構(gòu)造函數(shù) B::B(int b);

            B obj1(100);

            doSomething(obj1);   // ok

            doSomething(28);     // 如果沒聲明explicit可以,反之不行


            2. 條款三:盡可能使用const. Use const  whenever possible

            const實施于成員函數(shù)的目的,是為了確認該成員函數(shù)可作用于const 對象身上。它承諾絕不改變其對象的邏輯狀態(tài)。它可以使class的接口更加容易理解;而且,它們使操作const對象成為可能,因為改善c++效率的一個根本方法就是以pass by reference-to-const方式傳遞對象,而此技術(shù)的前提是我們有const成員函數(shù)可用來處理取得(并經(jīng)修飾而成)的const對象

            很多人都漠視的一個事實:如果兩個成員函數(shù)只是常量性(const 與否)不同,它們可以被重載。

            const和指針,要小心const和指針的關(guān)系

            如果一個指針*pconst對象的成員,我們不改變指針值p(也就是它指向哪個對象)而改變*p(它所指向?qū)ο蟮闹担?,那么編譯器不會對此提出異議。這同樣適用于const成員函數(shù)中的情況。 

            一個增加靈活性聲明:聲明為【mutable】的成員變量可能總是會被更改,即使在const成員函數(shù)內(nèi)

            const_cast<> 運算符可以用來轉(zhuǎn)換掉對象的const屬性

            請記?。?/span>

            <!--[if !supportLists]-->l         <!--[endif]-->將某些東西聲明為const可幫助編譯器偵測出錯誤用法。const可被施加于任何作用域內(nèi)的對象、函數(shù)參數(shù)、函數(shù)返回值類型、成員函數(shù)本體。

            <!--[if !supportLists]-->l         <!--[endif]-->編譯器強制實施bitwise constness,但你應(yīng)該使用conceptual constness 適當?shù)氖褂?/span>mutable和注意指針帶來的影響。

            <!--[if !supportLists]-->l         <!--[endif]-->const函數(shù)和non-con函數(shù)有實質(zhì)等價的實現(xiàn)時,可利用non-const函數(shù)調(diào)用const函數(shù)避免重復(fù)代碼。

             

            3. 條款四:確定對象在使用前已初始化. Make sure that object are initialized before they’re used.

            別混淆賦值(assignment)和初始化(initialization

            C++規(guī)定,對象的成員變量的初始化動作發(fā)生在進入構(gòu)造函數(shù)本體之前,應(yīng)該使用初始化列表實現(xiàn)。

            在構(gòu)造函數(shù)本體內(nèi)進行的只是賦值而不是初始化。初始化發(fā)生的事件更早,發(fā)生于這些成員的default構(gòu)造函數(shù)被自動調(diào)用之時(比進入構(gòu)造函數(shù)本體的時間更早)。這樣構(gòu)造函數(shù)所做的一切工作都浪費了。

            請立下一個規(guī)矩:總是在初始化列表中列出所有成員變量(包括內(nèi)置類型),以免還得記住哪些成員變量(如果它們在初值列中被遺漏的話)可以無需初值。

            當然,你可以將那些“賦值表現(xiàn)像初始化一樣好”的成員變量,改用賦值操作并移入一個private函數(shù),在所有的構(gòu)造函數(shù)中調(diào)用它,以避免不必要的編碼重復(fù)。這種做法在成員變量的初值系“由文件或數(shù)據(jù)庫”讀入時特別有用。

            C++有著十分固定的“成員初始順序”。Base早于derived,成員變量按聲明順序。

            對于non-local static對象,使用singleton是個不錯的方案

            請記住

            <!--[if !supportLists]-->l         <!--[endif]-->為內(nèi)置型對象進行手工初始化,C++不保證初始化他們

            <!--[if !supportLists]-->l         <!--[endif]-->構(gòu)造函數(shù)最好使用成員初始化列表,而不要在構(gòu)造函數(shù)本體內(nèi)賦值。其排列次序應(yīng)按照聲明次序

            <!--[if !supportLists]-->l         <!--[endif]-->Singleton實現(xiàn)non-local static對象


            4. 條款05 了解C++默默編寫并調(diào)用哪些函數(shù). Know what functions C++ silently writes and calls.

            為了駁回編譯器自動提供的功能(默認構(gòu)造函數(shù)、默認析構(gòu)函數(shù)、默認拷貝構(gòu)造函數(shù)、默認賦值操作符),可將相應(yīng)的成員函數(shù)聲明為private并且不予實現(xiàn)。

            將構(gòu)造函數(shù)聲明為explicit可以阻止隱式類型轉(zhuǎn)換.


            5. 條款07:為多態(tài)基類聲明virtual 析構(gòu)函數(shù). Declare destruction vritual in polymorphic base class.

            C++ 明確指出:當derived class對象經(jīng)由一個base class指針被刪除,而該base class帶有一個non-virtual析構(gòu)函數(shù),其結(jié)果是未定義——實際執(zhí)行時通常發(fā)生的事對象的derived成分沒被銷毀。

            消除這個問題的辦法很簡單,base class聲明一個virtual析構(gòu)函數(shù)。

            任何函數(shù)只要帶有一個virtual函數(shù)幾乎確定也應(yīng)該有一個virtual析構(gòu)函數(shù)。

            如果class不含virtual函數(shù),通常表示它并不意圖被用作一個base class

            無端地將所有class的析構(gòu)函數(shù)都聲明為virtual就像從未聲明它們?yōu)?/span>virtual一樣,都是錯誤的。很多人的心的事:只有當class內(nèi)含有至少一個virtual函數(shù)才將它聲明為virtual析構(gòu)函數(shù)。

            base class的析構(gòu)函數(shù)聲明為純虛函數(shù)是定義抽象類的常用手法,但是這個純虛析構(gòu)函數(shù)仍需提供定義,因為derived class需要調(diào)用base class的析構(gòu)函數(shù)。如果未定義則會導(dǎo)致鏈接錯誤。

             

            6. 條款12:Copy all parts of an object. 復(fù)制對象時勿忘其每一個成分

            derived class 應(yīng)在拷貝構(gòu)造函數(shù)的初始化列表和賦值運算符的函數(shù)體內(nèi)調(diào)用base class的拷貝構(gòu)造函數(shù)和賦值運算符。否則將導(dǎo)致初始化不完整。

            class Customer /* … */ };

            class PriorityCustomer : public Customer /* .. */}

            PriorityCustomer::PriorityCustomer(PriorityCustomer 
            &rhs) : Customer(rhs) {
                // 調(diào)用base class Customer拷貝構(gòu)造函數(shù)

                   
            /*

                   …

                   
            */


            }


            PriortiyCustomer
            & PriorityCustomer::operator=(PriorityCustomer &rhs){

                   Customer::
            operator=(rhs);   // 調(diào)用base class operator=

             

                   
            /*

                   …..

                   
            */


                  return *this;

            }



            7. 條款13:以對象管理資源. Use objects to manager resources.

            善于使用智能指針,為了防止資源泄漏,請使用RAII對象,他們在構(gòu)造函數(shù)中獲得資源并在析構(gòu)函數(shù)中釋放資源。

            兩個常用的RAII classestr1::shared_ptrauto_ptr。前者往往是較佳選擇(通過引用計數(shù)器實現(xiàn)),后者不支持指針的拷貝和復(fù)制,復(fù)制動作會使它(被復(fù)制物)指向null。

            類似的Boost庫中的boost::scoped_arrayboost::shared_array類也可以提供類似功能

            void test_fun()
            {

                 std::auto_ptr
            <Child> aptr_c(new Child("Child 1"));

                 Child 
            *ptr = new Child("Child 2");

                 aptr_c.
            get()->func();                  // 盡量使用顯示轉(zhuǎn)換,防止不合適的隱式轉(zhuǎn)換

                 (
            *aptr_c).func();                      // 使用隱式轉(zhuǎn)換,可以增強可讀性
             

                 std::auto_ptr
            <Child> aptr_c2 = aptr_c;  // will set aptr_c to null

                 
            // aptr_c->func();                      failed. aptr_c is null.

                 delete ptr;
            }


            智能指針auto_ptr可以在生命期結(jié)束時自動銷毀返回資源。盡量使用智能指針保存factory函數(shù)返回的指針。

            此外,利用棧對象自動銷毀調(diào)用析構(gòu)函數(shù)的特性,可以將需要釋放的資源根據(jù)作用域封裝在棧對象中,此棧對象即為一個資源管理對象,對應(yīng)類為資源管理類。


            8、條款14:在資源管理類中小心coping行為
            禁止復(fù)制。許多時候允許資源管理對象被復(fù)制是不合理的。應(yīng)該將coping行為聲明為private。

            對底層資源使用“引用計數(shù)器法”。
            tr1::shared_ptr是一個絕好的實現(xiàn)手段,這個類在vs2005的庫中還沒有被加入,vs2008的c++標準庫包含了這個類,當然tr1的大部分類實現(xiàn)來自于boost,這個也不例外。

            例如:

            class Lock{
            public:
                 
            explicit Lock(Mutex* pm) : mutexPtr(pm) 
                 
            {
                          
            lock(mutexPtr.get());
                 }

                 
            /* 
                    我們并沒有忘記析構(gòu)函數(shù),默認的析構(gòu)函數(shù)會自動調(diào)用每個非static類變量的析構(gòu)函數(shù)
                        當mutexPtr 的引用計數(shù)為0就會調(diào)用智能指針的刪除器
                
            */

            private:
                std::tr1::shared_ptr
            <Mutex> mutexPtr;
            };



            9、條款
            20:寧以pass-by-reference-to-constt替換pass-by-vaule

            除了熟知的,傳遞const引用會比單純的值傳遞效率更高,應(yīng)用傳遞還有意想不到的好處,多態(tài)。在參數(shù)為base class時,傳遞引用可以使虛函數(shù)被正確解析,而傳遞值將導(dǎo)致對象slicingbase class。

             在編譯器底層Reference往往是指針實現(xiàn)出來的,因此如果你有一個對象是內(nèi)置類型,傳遞value往往比reference要高效些。對內(nèi)置類型而言,當你有機會選擇采用pass-by-valuepass-by-reference-to-const時選擇前者并非沒有道理。這個忠告也適用于STL的迭代器和函數(shù)對象,因為他們習(xí)慣上被實現(xiàn)為passed-by-value。

            在函數(shù)返回對象時謹慎使用reference 。除了*this其他的返回引用最好仔細斟酌。

             
            請記住:決不要返回一個pointer或者reference指向一個local stack對象,或返回一個reference指向一個heap-allocated對象,或返回pointer或者reference指向一個local static對象而有可能需要多個這樣的對象。如果要,考慮singleton吧。

             
            10、條款22:將成員變量聲明為private

            將成員變量聲明為privateprotected不比public更具有封裝性。因為更改了protect變量將會影響雖有的derived class

             
            11、條款23:寧以non-membernon-friend替換member函數(shù)(尤其是需要滿足交換律的operator)

            C++不是純面向?qū)ο笳Z言,不是所有的函數(shù)都定義在類中。

            你不需要強行將所有函數(shù)定義在class內(nèi),因為這會造成class龐大的體積,而不同的用戶又可能對不同的方法感興趣。適當?shù)牟鸱质怯泻锰幍摹?/span>

             將所有的便利函數(shù)(uitlity funcation)放在多個頭文件中,但隸屬于同一個命名空間是C++標準庫的組織方式。

             因為在意封裝而讓函數(shù)成為classnon-member函數(shù)不意味它不可以是另一個classmember,它可以是某個工具類的static member函數(shù)。這讓純面向?qū)ο笏季S的java程序員感覺比較習(xí)慣。

             請記?。簩幙赡?/span>non-member non-friend函數(shù)替換member函數(shù)。這樣做可以增加封裝性、包裹彈性(packaging flexibility)和機能擴充性

             
            12、條款24:若所有參數(shù)皆需類型轉(zhuǎn)換,請為此采用non-member函數(shù)

            此條款一般用于實現(xiàn)operator時,為了滿足交換律和調(diào)用隱式類型轉(zhuǎn)換將operator在類外實現(xiàn)。是否聲明為friend看需要。

             
            13、條款
            34:區(qū)分接口繼承和實現(xiàn)繼承

            Derived classes內(nèi)的名稱會遮掩base classes內(nèi)的名稱,他們不會被重載。public繼承下從來沒有人希望如此。

            如何推翻C++對繼承而來名稱的缺省遮掩行為:

            使用using聲明式使被遮掩的base class函數(shù)可見

            using Base::funcation;

            有時候你并不想繼承base classes的所有函數(shù),這是可以理解的。但在public繼承下,這絕對不可能發(fā)生,因為它違反了public繼承所暗示的base classderived classes之間的is-a關(guān)系。(這也是為什么上述using 聲明被放在derived classpublic區(qū)域的原因:base classpublic名稱在derived class內(nèi)也應(yīng)該是public的)。然而在private繼承之下,它卻可能是有意義的。例如,假設(shè)Derivedprivate的形式繼承Base,以一個轉(zhuǎn)交函數(shù)完成對Base class函數(shù)的調(diào)用。(private是對Derived classes的一種協(xié)助)

             

             這也是為什么在copy constructorcopy assignment中必須調(diào)用base class的內(nèi)容的原因。因為它們被drived class的隱藏了,不會被繼承。

            條款12

             
            14、條款
            34:區(qū)分接口繼承和實現(xiàn)繼承

            pure virtual函數(shù)、impure virtual函數(shù)、non-virtual函數(shù)之間的差異,使得你得以精確指定你想要derived class繼承的東西:只繼承接口(pure virtual),或是繼承接口和一份缺省實現(xiàn)(virtual),或是繼承接口和一份強制實現(xiàn)(non-virtual)。

            pure virtual函數(shù)只具體指定接口繼承

            impure virtual函數(shù)具體指定接口繼承及實現(xiàn)繼承

            non-virtual 函數(shù)具體指定接口繼承以及強制性實現(xiàn)繼承,non-virtual函數(shù)會為該class建立起一個不變性,凌駕其特異性。你最好絕不重新定義繼承而來的non-virtual函數(shù)。

            否則如:

            class D{

            public:

            void mf();

            }


            class D : public B / *… */ };

            D x;

            *pD = &x;;

            *pB = &x;

            pD
            ->mf();

            pB
            ->mf();

            這兩個調(diào)用將調(diào)用不同的mf,因為mf是靜態(tài)綁定的,而不像virtual是動態(tài)綁定的。

            就是因為同樣的道理,一個derived class絕不應(yīng)該重新定義一個繼承而來的non-virtual析構(gòu)函數(shù)。

             
            15、條款35:考慮virtual函數(shù)以外的其他選擇

            Non-Virtual Interface手法
            一個有趣的思想流派主張virtual函數(shù)應(yīng)該總是被實現(xiàn)為private。這一基本設(shè)計,也就是“令客戶通過public non-virtual函數(shù)間接調(diào)用private virtual函數(shù)”,稱為non-virtual interface手法。是template method設(shè)計模式的一個特例。Effective C++的作者把這個non-virtual函數(shù)成為virtual函數(shù)的wrapper。

            NVI函數(shù)的一個優(yōu)點是可以在執(zhí)行virtual函數(shù)的特化行為之前和之后,做一些公共的初始化和清理工作。如果讓客戶直接調(diào)用virtual函數(shù)就沒有任何好辦法做這些事。

            有些事情看似比較怪異,你在derived class中實現(xiàn)一個derived class從不調(diào)用的virtual函數(shù)。但這并不存在矛盾。重新定義表示某些事情如何被完成,而調(diào)用他們表示何時被完成,base class只是保留了“函數(shù)合適被調(diào)用的”權(quán)利。一開始這些聽起來有些詭異,但是C++這種derived class”可重新定義被繼承來的private virtual函數(shù)“的規(guī)則完全合情合理。

            另外一種情況,使用Strategy模式
            使用函數(shù)指針代替virtual函數(shù),這樣是Strategy模式的一個簡單應(yīng)用。這樣做的優(yōu)點是同一個類型的實體可以有不同的運算實現(xiàn)方式(實體作為參數(shù)傳入)。但這樣做,指針指向的函數(shù)只能訪問public內(nèi)容,如果要訪問private內(nèi)容只能降低封裝。

            便利設(shè)施:借由boost和tr1::funcation實現(xiàn)Strategy模式會得到高于函數(shù)指針的彈性(flexibility)。


            16、條款
            37:絕不重新定義繼承而來的缺省參數(shù)值

            缺省參數(shù)是靜態(tài)綁定的,如果在virtual函數(shù)內(nèi)改變,將會出現(xiàn)函數(shù)調(diào)用和參數(shù)不符合的結(jié)果。如果在non-virtual…條款34里貌似說過不要改變non-virtual函數(shù)的實現(xiàn)

             
            18、條款39:明智而審慎地使用private繼承

            Private 繼承意味著 is-implemented-in-terms of(根據(jù)某物實現(xiàn)出)。它通常比復(fù)合(組合)的級別低。但是,當derived class要重新定義繼承而來的virtual函數(shù)時或者要訪問protected base class的成員時,這種設(shè)計是合理的。

            和復(fù)合不同,private繼承可以造成empty base的最優(yōu)化。這對致力于“對象尺寸最小化”的程序開發(fā)者(特別是嵌入式程序員)而言,可能很重要。

             
            19、條款40:明智而審慎的使用多重繼承

            對于一個從Java陣營轉(zhuǎn)入C++的程序員而言多重繼承一般不會被使用。

             如果確實需要,請注意“鉆石”繼承的出現(xiàn),即一個子類的繼承鏈上重復(fù)出現(xiàn)了同一個base class,這樣base class的成員變量會經(jīng)每一條路徑被復(fù)制。解決的方式是virtual繼承,但是virtual繼承會增加大小、速度、初始化(及賦值)復(fù)雜度成本。如果virtual base classes不帶任何數(shù)據(jù),將是最具使用價值的情況。

             多重繼承的確有正當用途。其中一個情節(jié)設(shè)計“public繼承某個Interface class”和“private繼承協(xié)助實現(xiàn)某個class”的兩相組合。

             
            20、條款
            42:了解typename的雙重意義

            聲明templateclasstypename可以互換。

            但是考慮這種情況:

            template<typename C>

            void print2md(const C& container)
            {

                   C::const_iterator
            * x;

            }


            C
            是在編譯器被解析的,而在編譯期編譯器并不知道C里面到底有什么。一種很不幸的事實,萬一C中有一個變量是const_iterator,而有一個全局變量為x。這會是一場災(zāi)難。一種避免的方案就是使用typename聲明這是一個類型
            template<typename C>

            void print2md(const C& container)
            {

                   Typename C::const_iterator
            * x;

            }

            請記?。?/span>

            聲明template參數(shù)時,前綴關(guān)鍵字classtypename可互換

            請使用關(guān)鍵字typename標示嵌套從屬類型名,但不能在base class listsmember initialization list內(nèi)以它作為base class修飾符。

             

             

            Virtual void mf1()
            {

                   Base::mf1();           
            // 最好暗自轉(zhuǎn)換成inline

            }

            posted on 2009-03-29 10:54 弱水一瓢 閱讀(332) 評論(0)  編輯 收藏 引用

            <2025年7月>
            293012345
            6789101112
            13141516171819
            20212223242526
            272829303112
            3456789

            導(dǎo)航

            統(tǒng)計

            文章分類

            最新評論

            18岁日韩内射颜射午夜久久成人| 91精品国产91久久综合| 国产精品久久久天天影视香蕉 | 久久久亚洲欧洲日产国码二区| 蜜桃麻豆WWW久久囤产精品| 亚洲va中文字幕无码久久不卡 | 99久久er这里只有精品18| 99久久国产热无码精品免费| 91精品婷婷国产综合久久| 久久精品国产色蜜蜜麻豆| 久久久精品国产sm调教网站| 日本精品一区二区久久久| 久久精品国产精品亚洲毛片| 亚洲国产成人精品久久久国产成人一区二区三区综 | 欧美亚洲另类久久综合| 91精品国产高清久久久久久国产嫩草| 色欲久久久天天天综合网精品| 久久国产成人精品麻豆| 欧美伊人久久大香线蕉综合 | 久久国产V一级毛多内射| 伊人久久大香线蕉综合5g| 精品综合久久久久久888蜜芽| 久久综合一区二区无码| 久久er热视频在这里精品| 久久九九兔免费精品6| 精品久久久久一区二区三区| 欧洲精品久久久av无码电影| 久久精品一区二区三区AV| 久久亚洲国产精品五月天婷| 99久久国产亚洲高清观看2024| 久久久久亚洲精品天堂| 久久精品国产精品亚洲精品| 看全色黄大色大片免费久久久 | 一本伊大人香蕉久久网手机| 亚洲av成人无码久久精品| 久久久久亚洲AV无码观看| 一本久久免费视频| 久久频这里精品99香蕉久| 四虎影视久久久免费观看| 亚洲伊人久久综合影院| 武侠古典久久婷婷狼人伊人|