More Effective C++ - 章節一 : 基礎議題

1. 仔細區分 pointers 和 references express

references和pointers的差異描述以下:數組

pointer:當須要考慮"不指向任何對象"時,或者是考慮"在不一樣時間指向不一樣對象"的能力時,應該採用pointer。前一種狀況能夠將pointer設爲null,後一種能夠改變pointer所指對象。緩存

reference:當肯定"老是會表明某個對象",並且"一旦表明了該對象就不能再改變",那應該使用reference。 reference不能爲空,所以無需作判空操做,相對pointer使用效率更高。某些操做符重載 operator[],operator= 採用reference實現。安全

結論: 當知道須要指向某個東西,並且毫不會改變指向其餘東西,或是當實現一個操做符而其語法需求沒法由pointers實現,就應選擇references。 其任什麼時候候,請採用 pointers。函數

2. 最好使用 C++ 轉型操做符 spa

因爲舊式的c轉型幾乎容許任何類型轉換爲任何其餘類型,這樣是十分拙劣的,若是每次轉型可以精確地指明轉型意圖,會更好。還有就是舊式轉型難以辨識,致使查看代碼時,會遺漏轉型操做。C++ 引進4個新的轉型操做符:指針

舊式 C 轉型: code

(type) expression

C++ 轉型: 對象

``` 1. static_cast (expression) : 基本擁有與 C 舊式轉型相同的威力和意義
  1. dynamic_cast (expression) :
    用來執行繼承體系中"安全的向下轉型或者跨系轉型動做",咱們能夠利用dynamic_cast,將 "指向父類對象的指針或引用" 轉型爲 "指向子類對象的指針或者引用"
    ,並得知是否轉型成功,轉型失敗會返回一個null指針(當轉型對象時指針)或一個exception(當轉型對象時reference)
    繼承

  2. const_cast (expression) :
    改變表達式常量性(constness)或者 變易性(volatileness)

  3. reinterpret_cast (expression) :
    與編譯平臺相關,不具備移植性。最經常使用用途是轉換 "函數指針" 類型,不到非用不可的地步不用,由於某些狀況下會出現轉型不正確。

例如:
typedef void (*FuncPtr)();
FuncPtr funcPtrArrary[10];

int doSomething();

funcPtrArrary[0] = reinterpret_cast (&doSomething); // 將返回值爲 int 的函數指針轉換爲返回值爲void*

<p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微軟雅黑';">若是你想爲一個不涉及繼承機制的類型執行轉型動做,可以使用static_cast;要改變常量性,必須使用const_cast;涉及繼承機制,使用dynamic_cast 或者 static_cast; reinterpret_cast 把一個指針轉換成一個整數,也能夠把一個整數轉換成一個指針。</p>



<p style="color: #AD5D0F;font-weight: bold;font-size: 20px; font-family: '微軟雅黑';">3. 絕對不要以多態的方式處理數組 </p>
------ 
<p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微軟雅黑';">大體代碼以下:</p>

// 簡單繼承關係
class BST{...};
class BalancedBST: public BST{...};

// 打印接口

void printfBSTArray(ostream& s, const BST arrry[], int numElements)
{
for(int i = 0; i < numElements; ++i){
s << array[i];
}
}

BalancedBST bBSTArray[10];
...
printfBSTArray(cout, bBSTArray, 10); // 發生不可預期問題

<p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微軟雅黑';">上述代碼中,<font color="#ff0000">編譯器爲了可以訪問整個數組,編譯器必須有能力決定數組對象大小,編譯器認爲大小爲BST類大小,可是咱們傳入的是其子類,而子類對象大小確定是大於父類的,所以致使這裏會發生不可預期錯誤</font>。 </p>

<p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-weight: bold; font-family: '微軟雅黑';">多態和指針算術不能混用。而數組對象幾乎老是會涉及指針的算術運算,因此數組和多態不要混用。</p>


<p style="color: #AD5D0F;font-weight: bold;font-size: 20px; font-family: '微軟雅黑';">4. 非必要不提供 default constructor </p>
------ 

<p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微軟雅黑';">由不帶參數的構造函數,或者爲全部的形參提供默認實參的構造函數,被稱爲默認構造函數(default constructor)。</p>

class foo
{
public:
foo(); // 默認構造函數
...
};

<p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微軟雅黑';">在一個完美世界中,凡能夠"合理的從無到有生成對象"的class,都應該內含默認構造函數,而"必須有某些外來信息才能生成對象"的class,則沒必要擁有默認構造函數。<font color="#ff0000">可是,當一個類缺少default constructor,使用時會受到限制。</font></p>

class foo
{
public:
foo(int Id); // 構造函數
...
};

foo bestfoo[10]; // 錯誤! 沒法調用構造函數
foo *best = new foo[10]; // 錯誤! 沒法調用默認構造函數

<p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微軟雅黑';">有介紹三種辦法來解決無默認構造函數的限制:</p>

<p style="font-size: 15px; letter-spacing:1px; font-family: '微軟雅黑';">1. non-heap 數組</p>

foo fooTest[] = {foo(1), foo(2), foo(3)}; // 構造函數得到id,能夠成功

<p style="font-size: 15px; letter-spacing:1px; font-family: '微軟雅黑';">2. 指針數組。 缺點:指針數組要刪除,不然內存泄漏;保存指針,浪費內存。</p>

typedef foo* foo_ptr;

foo_ptr bestfoo[10]; // 沒問題,存在的指針,不用調用構造函數

foo_ptr *best = new foo_ptr[10] // 也沒問題,數組堆上存的是指針

for(int i = 0; i < 10; ++i){
best[i] = new foo(Id number); // 傳入ID 初始化對象
}

<p style="font-size: 15px; letter-spacing:1px; font-family: '微軟雅黑';">3. placement new。分配內存時可指定內存位置。</p>

// 首先申請一塊適當大小的緩存,能夠是堆上的,也能夠是其餘特殊緩存(例如共享內存)
void *rawMemory = operator new;

// 讓 best 指針指向該塊內存首地址
foo* best = static_cast<foo*>(rawMemory);

// 而後利用 placement new 構造內存中的 foo 對象
for(int i = 0; i < 10; ++i){
new (&best[i]) foo(Id number); // (&best[i]) : 內存位置
}

<p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-weight: bold; font-family: '微軟雅黑';"><font color="#ff0000">缺少 default constructor 類的第二個缺點,將不適用於許多基於模板實現的容器類</font>。</p>

<p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-weight: bold; font-family: '微軟雅黑';"><font color="#ff0000">第三個缺點,虛基類沒有默認構造函數,將致使全部繼承類都要注意提供虛基類構造函數自變量,十分麻煩</font>。</p>

<p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微軟雅黑';">缺少默認構造雖然會致使上述三種限制,可是若是不須要默認構造函數的類卻加了默認構造函數,將致使該類內部member functions邏輯變得複雜,以及影響調用該類的客戶代碼的效率。 所以非必要不提供 default constructor。</p>

<p style="font-size: 15px;text-indent:60em;letter-spacing:1px; font-family: '微軟雅黑';">2018年10月1日14:59:02</p>
相關文章
相關標籤/搜索