Effective C++學習筆記-01

1.條款03:儘可能使用const
    1.1 const能夠修飾指針,指針所指物,二者或者都不是const對象。注意const的位置,位於*號左邊的是修飾所指物爲常量, *右邊是修飾指針
const char* p = greeting  //const data, non-const pointer
char* const p = greeting    //const pointer, non-const data
const char* const p = greeting // all the const

例子,以迭代器中的T*指針來講明具體的用法:
std::vector<int> vec;
...
const std::vector<int>::iterator iter = vec.begin(); // iter做用是T* const
*iter = 10;                        //能夠改變指針所指
iter++;                            //報錯,不能修改iter常量
std::vector<int>::const_iterator cIter = vec.begin();  //cIter做用是const T*
*cIter = 10;                        //不能夠修改指針指向
cIter++;                        //沒問題,能夠改變所指物

下列兩個函數的參數所返回的參數類型是一致的
void f1(const Widget* pw)   // f1和f2都返回了一個指針指向常量Widget
void f2(Widget const* pw)

    1.2 const成員函數: 經過聲明const成員函數有兩個好處,第一:使得class接口更容易被理    解,什麼內容是不能被修改的 第二:提高C++效率,pass-by-reference-to-const.    PS:兩個成員函數若是是常量,同樣能夠被重載。
    1.2.1 bitwise constness and logical constness


2.條款04:肯定對象被使用前已經被初始化Make sure that objects are initialized before they're used.
    2.1對於內置類型以外的對象的初始化,主要由構造函數來實現,因此確保每一個構造函數把全部的成員變量初始化。
    2.2【理解】注意不要混淆了初始化(initialization)和賦值(assignment),參考下面的例子:
    賦值操做:
    class PhoneNumber{...};
    class ABEntry{
    private:
        std::string theName;
        std::string theAddress;        
        std::List<PhoneNumber> thePhones;
        int numTimesConsulted;
    public:
        ABEntry(const std::string& name, const std::string& address, const std::List<PhoneNumber>& phones);    
    };
    //構造函數進行賦值(assignments)而並不是是初始化(initializations)
    ABEntry::ABEntry(const std::string& name, const std::string& address, const std::List<PhoneNumber>& phones){
        theName = name;   
        theAddress = address
        thePhones = phones;
        numTimesConsulted = 0;

    }
    //構造函數初始化的最佳寫法是使用MemberInitialization List(成員初始化列表)的方式實現
    ABEntry::ABEntry(const std::string& name, const std::string& address, const std::List<PhoneNumber>& phones)
    : theName(name), theAddress(address), thePhones(phones), numTimesConsulted(0)
    {}

    這個構造函數比上面的效率更高!上面那個基於賦值的那個構造函數要首先調用一個default默認構造函數設置成員變量初始值。
    
    2.3 爲免除「跨編譯單元之初始化次序」問題,以local static對象替換non-local static對象
    

第二:構造函數,析構函數,賦值

1.條款05 瞭解C++默默調用了哪些函數 Know what functions C++ silently writes and calls
    1.1 空類Empty class,C++處理以後會爲它添加默認的構造函數,一個賦值操做符和一個析構函數。
    若是你寫了 class Empty{}; 通過C++處理後至關於:
    class Empty{
    public:
        Empty(){...};             //default構造函數
        Empty(const Empty& rhs){..};    //copy構造函數
        ~Empty(){...};            //析構函數,是否爲Virtual?
        Empty& operator=(const Empty& rhs){} //copy assignment操做符


    }
【請記住】:編譯器暗自爲class建立default構造函數,copy構造函數,析構函數和copy assignments操做符。


2.條款06 若不想使用編譯器自動生成的函數,就該明確拒絕。
爲了避免使用編譯器自動提供建立函數的功能,可將相應的成員函數聲明爲private而且不予實現。使用像Uncopyable這樣的的base class也是一個方法。

//Uncopyable實現阻止編譯器自動建立函數
class Uncopyable{
protected:
    Uncopyable(){};
    ~Uncopyable(){};
private:
    Uncopyable(const Uncopyable&); //阻止copy構造函數
    Uncopyable& operator=(const Unconpyable&);
}

爲了防止HomeSale對象被拷貝,經過繼承Uncopyable類即可以免
class Homesale: private Uncopyable{
...                    //class將再也不自動聲明 copy構造函數以及copy assignment操做符。
...
}



3. 條款07 爲多態基類聲明virtual析構函數(Declare destructors virtual in polymorphic base classes)

【請記住】:
    3.1 Polymorphic(帶多態性質的)的基類base class,應該聲明一個virtual析構函數。若是一個class存在任何一個virtual函數,避免由於子類繼承過程當中帶來的問題。應該擁有聲明一個virtual析構函數。
    3.2 Classes設計的目的若是不是爲了實現多態做爲基類來使用,就不應聲明virtual析構函數。


錯誤使用方法    
class SpecialString: public std::string{    //餿主意!std::string中有個non-virtual函數
    ....

};

SpecialString* pw = new SpecialString("Impending Doom");

std::string* ps;
...
ps = pw; //SpecialString* = string*

delete ps; //未定義!*ps的SpecialString資源會泄露
       //由於SpeicalString的析構函數沒被調用

4. 條款08 別讓異常逃離析構函數(Prevents exceptions from leaving deconstructors)
【請記住】
    4.1 析構函數絕對不要吐出異常,若是一個被析構函數調用的函數可能拋出異常,那麼析構函數應當捕捉任何異常,而後吞下他們或者結束程序。
    4.2 若是客戶須要對某個操做函數運行期間拋出的異常作出反應,那麼class應當提供一個普通函數(而非在析構函數中)處理。
相關文章
相關標籤/搜索