天天看點

Effective C++筆記 —— 第五章

  1. 轉型

    1.1 const_cast 通常被用來将對象的常量性轉除,它也是唯一具有此能力的C++轉型操作符

    1.2 dynamic_cast 主要用來執行“安全向下轉型”(父類轉為子類),也就是用來決定某對象是否歸屬繼承體系中的某個類型。它是唯一無法由舊式文法執行的動作,也是唯一可能耗費重大運作成本的轉型動作

    1.3 reinterpret_cast 意圖執行低級轉型,實際動作及結果可能取決于編譯器,這也就表示它不可移植。例如将一個pointer to inta 轉型為一個int。這一類轉型在低級代碼以外很少見

    1.4 static_cast 用來強迫隐式轉換,例如将non-const 對象轉為const 對象,或将int轉為double等等,但是無法将const轉為non-const,這個是有const_cast才辦的到

  2. Derived d;

    base* pb=&d;

    有時候上述兩個指針并不相同,這種情況下會有一個偏移量在運作期被施行于Derived* 指針上,用以取得正确的base* 指針值

    是以單一的對象可能擁有一個以上的位址(例如以base指向它時的位址和以derived指向它時的位址)

    對象的 布局方式和它們的位址計算方式随編譯器的不同而不同,那意味着“由于知道對象如何布局”而設計的轉型,在某一平台行得通,在其他平台并不一定行得通

  3. 之是以需要dynamic_cast,通常是因為你想在一個你認定為derived class對象身上執行derived class操作函數,但你的手上卻隻有一個指向base的指針或引用,你隻能靠他們來處理對象。一般都兩個做法可以避免這種做法,第一,使用容器并在其中存儲直接指向derived class對象的指針;第二,可通過base class接口處理“所有可能之各種window派生類”,那就是在base class内提供virtual 函數做你想對各個window派生類做的事,舉個例子,雖然隻有specialwindow可以閃爍,但是将閃爍函數聲明于base class内并提供一份什麼也沒做的預設實作代碼是有意義的
  4. 如果可以,盡量避免轉型,特别是在注重效率的代碼中避免dynamic_cast;如果轉型是必要的,試着将它隐藏在某個函數背後,客戶随後可以調用該函數,而不需要将轉型放進他們自己的代碼内;甯可用新式轉型,不要使用舊式
  5. 避免傳回handles(包括references、指針、疊代器)指向對象内部
    class Point{
    	void setX(int newVal);
    	void setY(int newVal);
    };
    struct RectData{
    	Point ulhc;
    	Point urhc;
    };
    class Rectangle{
    	shared_ptr<RectData> pData;
    	Ponit& upperLeft() const {return  pData->ulhc;}
    };
               
    這個設計是沖突的,一方面upperLeft被聲明為const成員函數,另一方面卻傳回reference指向private内部資料,調用者于是可以通過這些reference更改内部資料
    Point coord1(0,0);
    Point coord2(100,100);
    const Rectangle res(cood1,cood2);
    rec.upperLeft().extX(50);
               
    降低了封裝性,可以做如下更改
    class Rectangle{
    	shared_ptr<RectData> pData;
    	const Ponit& upperLeft() const {return  pData->ulhc;}
    };
               
    有了這樣的改變,客戶可以讀取矩形的Points,但是不能塗寫他們。但即便如此,upperLeft還是傳回了“代表對象内部的”handles,有可能導緻空懸的handles:這種handles所指的東西不複存在
    class GUIObject {...};
    const Rectangle boundingBox(const GUIObject& obj);  //以by value傳回一個矩形
    };
               
    現在,客戶有可能這麼使用這個函數
    GUIObject* pgo;
    const Point* pUpperLeft=&(boundingBox(*pgo).upperLeft());
               
    對boundingBox的調用獲得一個新的、暫時的Rectangle對象,這個對象沒有名稱,是以我們權且稱它為temp。随後upperLeft作用于temp身上,傳回一個reference指向temp的一個内部成分,更具體底說是指向一個用以标示temp的Points。于是pUpperLeft指向那個Point對象。目前一切還好,但是在那個語句結束之後,temp将被銷毀,temp内的Points将被析構,最終導緻pUpperLeft指向一個不存在的對象
  6. inline造成的代碼膨脹可能導緻額外的換頁行為,降低指令高速緩存裝置的擊中率。inline函數通常一定被置于頭檔案中,因為大多數建置環境在編譯過程中進行inline,而為了将一個函數調用轉換為被調用函數的本體,編譯器必須知道那個函數長什麼樣子。inline在大多數C++程式中是編譯期行為。
  7. templates通常也被置于頭檔案内,因為它一旦被使用,編譯器為了将它具現化,需要知道它長什麼樣子
  8. virtual函數不能是inline,因為virtual 函數在執行期才知道調用哪個函數,inline在執行前就将調用動作替換為被調用函數的本體