天天看點

Effective C++筆記簡易總結以及程式示範

4、确定對象被使用前已被初始化

在使用變量時不進行初始化是不好的行為,在程式中有可能讀入一個未初始化的值就可能導緻程式崩潰。

對于内置類型,保證在使用對象之前進行初始化:

int x = 0;
const char* text = "A C-style string"

double d;
std::cin >> d;           

對于結構體成員的初始化,要差別初始化和指派的差別:

class PhoneNumber
{
private:
    int name;
    int number;
public:
    PhoneNumber(){name = 0;number = 0;} //這裡是指派不是初始化
    PhoneNumber():name(0),number(0){} //這裡才是初始化(成員初值列)
};           

在C++中,對象的成員變量的初始化動作發生在進入構造函數本體之前,上面利用成員初值列的方法初始化的效率較高,同時不浪費default構造函數做的一切

另外一種情況,C++對“定義于不同編譯單元内的non-local static對象”的初始化相對次序并無明确定義。也就是如果某編譯單元内的某個non-loacl static對象的初始化動作使用了另一個編譯單元内某個non-local static對象,而它所用到的這個對象可能尚未被初始化,如果出現這種情況,那麼問題是緻命的。

解決這個的方法就是将每個non-loacl static對象搬到自己的專屬函數内(該對象在此函數内被聲明為static)。這些函數傳回一個reference指向它所含的對象。然後使用者調用這些函數,而不直接指涉這些對象。這個手法的基礎在于:C++保證,函數内的local static對象“會在該函數被調用期間”、“首次遇上該對象之定義式”時被初始化:

class FileSystem{...};
FileSystem& tfs()           //這個函數用來替換tfs對象;它在FileSystem class            
{                  //中可能是個static。定義并初始化一個local static對象,                  
    static FileSystem fs;   //傳回一個reference指向上述對象。
    return fs;
}
class Directory{...};
Directory::Directory(params)
{
    ...
    std::size_t disks = tfs().numDisks(); //調用對象
    ...
}
Directory& temDir()  //定義temDir對象為“函數對象”
{
    static Directory td;
    return td;
}           

如此,在調用temDir對象時,C++會對tfs進行初始化,并對temDir也進行初始化

5、了解并拒接默默調用的函數

空類并不是空類,C++編譯器會自動為其聲明一個copy構造函數、一個copy assignment操作符和一個析構函數,如果你沒有聲明任何構造函數,那麼編譯器也會為你聲明一個default構造函數(這些函數都是public且inline的)。

這些會引起一些比較容易忽視的問題:比如通過copy assignment操作符去給一個類指派一個擁有不能進行指派成員的類時會引起編譯器的警告(類中有引用成員,将這個類用過=号賦予給另一個類時會出現錯誤,因為C++不允許reference改變指向對象)

解決這個“麻煩”的方法,即不想使用編譯器自動生成的函數。手動聲明copy構造函數和copy assignment操作符,将其為private:

class Uncopyable
{
protected:
    Uncopyable(){}
    ~Uncopyable(){}
private:
    Uncopyable(const Uncopyable&);
    Uncopyable& operator=(const Uncopyable&);
};

class HomeForSale:private Uncopyable{
    ...
};           

這樣,任何想嘗試拷貝HomeForSale對象的行為編譯器會進行報錯。

6、别讓異常逃離析構函數

析構函數絕對不要吐出異常。如果一個被析構函數調用的函數可能抛出異常,析構函數應該捕捉異常,然後吞下他們(不傳播)或者結束程式;

如果客戶需要對某個操作函數運作期間抛出的異常做出反應,那麼class應該提供一個普通函數(而非在析構函數中)執行該操作。

22、将成員變量名聲明為private

class AccessLevels
{
public:
    int getReadOnly() const {return readOnly;}
    void setReadWrite(int value) {readWrite = value;}
    int getReadWrite() const { return readWrite;}
    void setWriteOnly(int value) {writeOnly = value;}

private:
    int noAccess; //對此int無任何通路
    int readOnly; //對此int做隻讀通路
    int readWrite; //對此int做讀寫通路
    int writeOnly; //對此int做隻能寫通路
};           

寫成上述這樣是值得建議和安全的,有以下好處:

1、完美的封裝性,不管你的成員變量以後怎麼變使用者都不會知道;

2、可以為“所有可能的實作”提供彈性。

類中所有的成員變量都應該為private,不管是public還是protected都是不安全的,因為一個可以被所有人通路,另一個可以被所有“親戚”通路,protected相比較public并不具有多好的隐秘性。

繼續閱讀