effective C++ 讀書筆記(上)

條款05:瞭解C++默默編寫並調用那些函數

      在類的聲明中,沒有聲明而會由編譯器聲明的有:一個拷貝構造函數,一個拷貝賦值運算符和一個構造函數。也就是說,如果聲明瞭一個帶參的構造函數,那麼編譯器將不會爲你聲明一個無參的構造函數,因此在聲明類的對象時必須調用含參數的構造函數,否則將會編譯出錯。如果沒有聲明這個帶參的構造函數,反而不會出現這樣的錯誤,這必須十分注意。拷貝賦值運算符使用時,如果類的成員裏有引用變量或者常量,那麼由於構造函數已經爲類的這些成員初始化了,這些成員不能再被賦值,此時編譯器就會拒絕生成拷貝賦值運算符。這個時候就需要自己聲明並定義一個拷貝賦值運算符了。

 

條款06:若不想使用編譯器自動生成的函數,就該明確拒絕

      作者在書裏介紹了一種拒絕使用編譯器自動生成的函數的方法,比如拷貝構造函數和拷貝賦值運算符。爲了不讓一些對象拷貝動作發生且在編譯期就阻止它。作者定義了一個基類,派生類以私有形式繼承該基類,那麼基類的拷貝構造函數將不能被派生類調用,這樣在對派生類對象進行拷貝操作時,自動調用基類拷貝函數的動作被認爲是非法的,這樣派生類的拷貝也無法進行。

 

條款13:以對象管理資源

      有時候需要將資源放進對象內,這樣可以藉助對象的析構函數來確保資源被釋放。這裏介紹了智能指針auto_ptr。auto_ptr被當作一般指針使用,但是該指針被銷燬時將會自動刪除它指向之物。使用它必須十分小心,不能讓多個指針指向一個已經由auto_ptr指向的對象。爲此,auto_ptr被賦予了一個特殊的性質:若通過拷貝構造函數或拷貝賦值操作符複製它們,它們會變成null,而複製所得的指針將取得資源的唯一擁有權。

 

      這在一些情況下是不能被接受的。因此就有了另一個方案:“引用計數型智慧指針”(RCSP)。所謂RCSP也是一個智能指針,持續追蹤共有多少對象指向某筆資源,並在無人指向它時自動刪除該資源。TR1的tr1::shared_ptr就是一個RCSP。

 

條款14:在資源管理類中小心複製行爲

      這裏書的作者提到了一個RAII的概念,它的字面意思是“資源獲取即初始化”。

      tr1:shared_ptr允許指定所謂的“刪除器”,那是一個函數或函數對象,當引用次數爲0時便被調用。刪除器對tr1:shared_ptr構造函數而言是可有可無的第二參數,所以代碼看起來像這樣:

 

 

條款15:在資源管理類中提供對原始資源的訪問

      tr1::shared_ptr和auto_ptr都提供一個get成員函數,用來執行顯式轉換,也就是它會返回只能指針內部的原始指針(的復件)。和(幾乎)所有智能指針一樣,tr1::shared_ptr和auto_ptr也重載了指針取值操作符,它們允許隱式轉換至底部原始指針。

      爲了實現自動類型轉換,這裏介紹了一種方法:

 

 

條款18:讓接口容易被正確使用,不易被誤用

      explicit關鍵字。該關鍵字用在單參數(不包含默認參數)的構造函數前,用以阻止對該類的隱式轉換。

 

條款24:若所有參數皆需類型轉換,請爲此次啊用non-member函數

      Scott(書的作者)提到:如果你需要爲某個函數的所有參數(包括被this指針所指的那個隱喻參數)進行類型轉換,那麼這個函數必須是個non-member。

 

條款25:考慮寫出一個不拋異常的swap函數

      先看如下的代碼:

 

      一些細節並不重要,重要的部分是這個swap函數針對Widget特化了。“這個函數一開始的“template<>”表示它是std::swap的一個全特化版本,函數名稱之後的“<Widget>”表示這一特化版本系針對“T是Widget”而設計。換句話說當一般性的swap template施行於Widget身上便會啓用這個版本。通常我們不能夠改變std命名空間內的任何東西,但可以爲標準template製造特化版本,使它專屬於我們自己的classes。”【P107】

 

條款27:儘量少做轉型動作

      C++還提供了四種新式轉型:

      const_cast<T> (expression)

      dynamic_cast<T> (expression)

      reinterpret_cast<T> (expression)

      static_cast<T> (expression)

      各有不同的目的:

  • const_cast通常被用來將對象的常量性移除。它也是唯一有此能力的C++-style轉型操作符。
  • dynamic_cast主要用來執行“安全向下轉型”,也就是用來決定某對象是否歸屬繼承體系中的某個類型。它是唯一無法由舊式語法執行的動作,也是唯一可能耗費重大運行成本的轉型動作。
  • reinterpret_cast意圖執行低級轉型,實際動作(結果)可能取決於編譯器,這也就表示它不可移植。例如將一個pointer to int轉型爲一個int。這一類轉型在低級代碼意外很少見。
  • static_cast用來強迫隱式轉換,例如將non-const對象轉爲const對象,或將int轉爲double等等。它也可以用來執行上述多種轉換的反向轉換,例如將void*指針轉爲typed指針,將pointer-to-base轉爲pointer-to-derived。但它無法將const轉爲non-const——這隻有const-cast能辦到。

      以下的代碼是很有趣的:

 

      這段程序將*this轉型爲Window,對函數onResize的調用也因此調用了Window::onResize。但恐怕你沒有想到,它調用的並不是當前對象上的函數,而是稍早轉型動作所創建的一個“*this對象之base class成分”的暫時副本身上的onResize!那麼如果Window::onResize修改了對象內容,當前對象其實沒被改動,改動的是副本。然而SpecialWindow::onResize內如果也修改對象,當前對象會被改動。所以正確的應該是如下代碼:

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章