Effective C++之條款3,條款4筆記整理

來源:互聯網
上載者:User

條款03: 儘可能使用const

本條款總結了const的使用情境和使用它帶來的好處。

關鍵字const多才多藝。

char greeting[]="hello":char *p=greeting;//non-const pointer,non-const dataconst char* p=greeting;//non-const pointer,const datachar* const p=greeting;//const pointer,non-const dataconst char* const p=greeting;//const pointer,const data

const文法雖然變化多端,但並不莫測高深,如果關鍵字const出現在星號左邊,表示被指物是常量,如果出現在星號右邊,表示指標自身是常量;如果出現在星號兩邊,表示被指物和指標兩者都是常量。

如果被指物是常量,有些程式員會將關鍵字const寫在類型之前,有些人會把它寫在類型之後、星號之前。這兩種寫法的意義相同,所以下面兩個函數接收的參數類型是一樣的:

void f1(const Widget* pw);void f2(Widget const *pw);

 

下面是STL中vector迭代器中const迭代器和const_iterator的區別

const std::vector<int>::iterator iter=v.begin();*iter=10;//正確++iter;//錯誤

 

std::vector<int>::const_iterator citer=v.begin();*citer=10;//錯誤++citer;//正確

const最具威力的用法是面對函式宣告時的應用。在一個函式宣告式內,const可以和函數傳回值、各參數、函數自身(如果是成員函數)產生關聯。

令函數返回一個常量值,往往可以降低因客戶錯誤而造成的意外,而又不至於放棄安全性和高效性。

例如:

class Rational{......};const Rational operator*(const Rational& lhs,const Rational& rhs);Rational a,b,c;(a*b)=c;if(a*b=c)...

const可以預防“沒意義的賦值操作”和“==意外換成=”的煩惱;

 

const成員函數

將const實施於成員函數的目的,是為了確認該成員函數可以用於const對象身上。這一類成員函數之所以重要,基於兩個理由。

1.它們使得class介面比較容易理解,得知哪個函數可以改動對象內容而哪個函數不行。

2.它們使“操作const對象”成為可能

class TextBlock{     public:              ...       const char& operator[](std::size_t position) const{    return text[position];}       char& operator[](std::size_t position){    return text[position];}    private:       std::string text;}//使用operator[]TextBlock tb("hello");std::cout<<tb[0];//調用non-const[]const TextBlock ctb("world");std::cout<<ctb[0];//調用const[]
std::cout<<tb[0];//正確tb[0]='x';//正確std::cout<<ctb[0];//正確ctb[0]='x';//錯誤

成員函數如果是const意味著什嗎?這兩個流行概念:bitwise constness和logical constness

bitwise const 陣營的人相信,成員函數只有在不更正對象之任何成員變數時才可以說是const,也就是說它不更改對象內的任何一個bit。

如果成員變數的是一個指標,但是後面改變了指標地址對應的內容,這就說不過去了。

下面使用mutable釋放掉non-static成員變數的bitwise constness約束

class CtextBlock{   public:            ...   std::size_t length() const;   private:       char* pText;      mutable std::size_t textLength;//這些成員變數總是會被更改,即使在const成員函數內      mutable bool lengthIsValid;};std::size_t CTextBlock::length() const{   if(!lengthIsValid)  {     textLength=std::strlen(pText);     lengthIsValid=true;   }   return textLength;}

此外,一個編程技巧是:當const 和non-const 成員函數有著實質等價的實現時,令non-const 版本調用const 版本可避免代碼重複

class TextBlock{   public:          ......    const char& operator[](std::size_t position) const{       ...       ...       ...       return text[position];}char& operator[](std::size_t position){     return const_cast<char&>(static_cast<const TextBlock&>(*this)[position]);}...};

這裡有兩次轉型,第一次用來*this添加const.第二次則是從const operator[]的傳回值移除const

請記住:

某些東西聲明為const可協助編譯器偵測出錯誤用法,const可被施加於任何範圍內的對象,函數參數,函數傳回型別、成員函數本體

編譯器強制實施bitwise constness,但你編寫程式時應該使用“概念上的常量性”

當const和non-const成員函數有著實質等價的實現時,令non-const版本調用const版本可避免代碼重複。
 

條款04: 確定對象被使用前已先被初始化

本條款告誡程式員,在C++程式設計中,應該對所有對象初始化,以避免不必要的錯誤,同時,給出了高效初始化對象的方法和正確初始化對象的方法。

 

(1)初始化建構函式最好使用成員初值列(member initialization list) ,而不要在建構函式本體內使用賦值操作(assignment) 。初值列出的成員變數,其排列次序應該和它們在class 中的聲明次序相同。

考慮一個用來表現通訊錄的class ,其建構函式如下:

ABEntry::ABEntry(const string&name,const string& address,const list<PhoneNumber>&phones){    theName=name;//這些都是賦值    theAddress=address;//並非初始化    thePhones=phones;    numTimeConsulted=0;}

 

ABEntry::ABEntry(const string& name,const string& address,const list<PhoneNumber>&phones):theName(name,theAddress(address),thePhones(phones),numTimesConsulted(0))//現在使用的是初始化{//建構函式本體不必有任何動作}

有些情況下即使面對的成員變數屬於內建類型(那麼其初始化與賦值的成本相同),也一定得使用初始化列表。如果成員變數是const或references,它們就一定需要初值,不能賦值。避免任何麻煩,任何情況下都使用初始化列表

C++ 有著十分固定的”成員初始化次序”。次序總是相同: base class早於其derived classes 被初始化,而class 的成員變數總是以其聲明次序被初始化。回頭看看ABEntry. 其theName 成員永遠最先被初始化,然後是theAddress,再來是thePhones,最後是numTimesConsulted。即使它們在成員初值列中以不同的次序出現(很不幸那是合法的),也不會有任何影響。

 

(2)C++ 對”定義於不同編譯單元內的non-local static 對象”的初始化次序並無明確定義。為免除”跨編譯單元之初始化次序”問題,請以local static 對象替換non-local static 對象。

所謂static對象,包括global對象,定義於namespace範圍內的對象、在class內,在函數內、以及在file範圍內被聲明為static的對象。函數內的static對象稱為local static對象(因為它們對函數而言是local),其他的static對象稱為non-local static對象。程式結束時static對象會被自動銷毀,也就是它們的解構函式會在main結束時被調用。

class FileSystem{...};FileSystem& tfs(){    static FileSystem fs;//以local static的方式定義和初始化object    return fs;}class Directory{...};Directory::Directory(params){   ...   std::size_t disks=tfs().numDisks();   ...}Director& tmpDir(){  static Directory td;  return td;}

 

為了避免在對象初始化之前過早的使用它們,你需要做三件事:

1.手工初始化內建型non-member對象

2.使用建構函式成員初始化列表初始化

3.在“初始化次序不確定性”氛圍下加強你的設計

請記住:

1.為內建對象進行手工初始化,因為C++不保證初始化它們

2.建構函式最好使用初始化列表,而不要在建構函式中使用賦值操作,其排列次序應該和它們在class中的聲明次序相同

3.為避免“跨編譯單元之初始化序”問題,請以local static對象代替non-local static 對象

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.