Featured image of post 【Effective Modern C++ 閱讀筆記 #1】ch4 - Smart Pointers 前言, 什麼是 dangling pointer?

【Effective Modern C++ 閱讀筆記 #1】ch4 - Smart Pointers 前言, 什麼是 dangling pointer?

介紹 raw pointer 的缺點 — 無法區分物件/陣列、需手動 delete、可能 dangling,smart pointer 解決這些問題。

ch4 - Smart Pointers 前言

raw pointer 的缺點

1. 以 raw pointer 而言,我們不知道他指向的是 single object 還是 array (表示法相同)

都是 object*,我們沒有辦法直接區分出來。

但為了簡化例子,我們以下使用 int 代替,但就沒辦法舉例到 single-object 的 delete。

  • 以下是 int array 的宣告
int* p = new int[0]; // accessing p[0] or *p is undefined
delete[] p; // cleanup still required
  • 以下是 int pointer 的宣告
#include <iostream>

using namespace std;

int main(){ 
    int n = 1;
    int* p1 = &n;
    cout << *p1 << endl;

    int a[3] = {1, 2, 3};
    int* p2 = a;
    cout << p2[1] << endl;

    int* p3 = new int[5]; // accessing p[0] or *p is undefined
    cout << p3[1] << endl;
    p3[1] = 10;
    cout << p3[1] << endl;
    delete[] p3; // cleanup still required

    return 0;
}

p1, p2 沒有使用到 new, 單純的拿地址,我們不需要寫 delete,
p3 使用了 new 宣告了 int array 這個 object,
我們需要手動 delete p3

  • 結果

【Effective Modern C++ 閱讀筆記 #1】ch4 - Smart Pointers 前言, 什麼是 dangling pointer?

2. raw pointer 我們需要手動進行 destroy,換言之我們可能會忘記要 delete

這個很直覺,有 new,就必須要在另外寫一個 delete,
這就不是很方便。

3. raw pointer 可能會有專用的 destruction,我們也必須傳入

我們應該要使用 delete,或使用 dedicated (專用的) destruction,
這是我們必須決定的。

4. 與第一點相同的,raw pointer 我們在 delete 時,是 single-object 或是 array ?

與第一點相同的,raw pointer 我們在 delete 時,是 single-object 或是 array ?

  • single-object 要使用 delete
  • 而 array 要使用 delete[]

因為表示方法相同,所以我們沒辦法知道。

5. (未知的行為) raw pointer 因為手動 delete 的關係,我們可能會有不小心多次 delete 的情況

這其實比想像中容易發生,對於同一個物件的多次 delete,可能會有未知的行為。

obj* a;
if(condition){
    // Do something...
    delete a;
}
delete a;

我們不知道可能會發生什麼事。

6. (未知的行為) raw pointer 可能會創造出 dangling pointer,也不知道會發生什麼事

我們先來定義 dangling pointer,
通常我們會有一個 pointer 指向一個物件 (與其對應的記憶體位置)

pointer -> object(與對應的 memory 位置)

當 object 基於某些原因已經被 destroy,而這個仍然指向他的 pointer 就會被稱作 dangling pointer

dangling pointer ->  destroy object(對應的 memory 位置)

這個 pointer 依然指向這個記憶體位置,如果這時候我們使用了對這個 pointer 取值,
會發生什麼事情我們無法預期。

總結上述,我們發現 raw pointer 有太多細節需要注意
雖然稍加注意就可以防範,但我們必須非常謹慎使用才行

smart pointer 的出現

C++11 後,smart pointer 的出現,
基本上我們可以視為他是「原來 raw pointer 的 wrapper」,
而這個 warpper,幫我們迴避掉了上面多數缺點,使我們可以減少犯下的錯誤。

因此在 C++ 11 以後,
我們大部分都會推薦優先使用 smart pointer,
smart pointer 不但可以做到幾乎全部 raw pointer 能做到的事情,
還可以大幅減少因為不小心犯下的錯誤。

smart pointer 的種類

我們總共有四種 smart pointert 出現在 C++11 之後,分別有

  • std::auto_ptr
  • std::unique_ptr
  • std::shared_ptr
  • std::weak_ptr

std::auto_ptr

其中,std::auto_ptr 已經被棄用,在 C++98 時因為 move semantics 還沒有出現,
因此他有使用 copy 的方式實現,總之就是還不完整設計,知道是個過渡性的東西就好,
後來在 C++ 11 後我們都使用 std::unique_ptr

std::unique_ptr 幾乎做到所有 std::auto_ptr 能做到的事情,
他還做得更多、更有效率,而且不是使用 copy 的方法

目前唯一我們還使用 std::auto_ptr 的情境就是為了配合 C++ 98 的 compiler,
不然建議一律改使用 std::unique_ptr

Reference

使用 Hugo 建立
主題 StackJimmy 設計