智慧型指標是 C++ 中一個常用的設計模式,它可以讓 C++ 的程式自行管理記憶體的配置與回收,避免記憶體洩漏等問題。

在 C/C++ 語言中,我們常常會使用指標(pointer)來配置或存取記憶體,一個指標變數儲存了記憶體的位址,而程式設計師就可以運用這個記憶體位址來做出各種變化,是一個非常好用的型別,甚至在某些複雜的應用上,如果沒有指標這項功能的話,可能會讓程式設計師不知道如何開發程式。

雖然指標對於 C/C++ 程式設計師而言非常重要,不過它難以管理的問題,也常常讓程式開發者頭痛,如果記憶體沒有配置得當,很容易造成懸置指標(dangling pointer)、空指標例外(null pointer exception)與記憶體洩漏(memory leak)等問題,嚴重的話會讓直接讓整個程式當掉、無法執行,而且記憶體配置與指標的問題在除錯上比較麻煩,編譯器並不會因為存取不對的記憶體位址而發出警告,必須要靠程式設計師自己小心的來處理。


假設有一段 C++ 程式碼如下:

Foo* foo = new Foo();
foo->Study();  // 如果這裡發生例外(exception)的話,
delete foo;    // 最後的記憶體將無法回收

如果在 Study() 函數中發生例外,讓程式提早跳出原有的執行順序的話,有可能就會造成這裡配置的記憶體永遠無法回收,直到程式執行結束為止,而這樣的問題就可以考慮以智慧型指標來解決。

智慧型指標(Smart Pointer)

智慧型指標其實就是把一般的指標包裝在 C++ 的物件之中,然後再加上一些記憶體管理的功能,而在使用上則跟一般的指標差不多,所以可以直接用來代替一般的指標。

智慧型指標的實作方式有許多種,這裡我們只是示範其中一種比較簡單的方式。其最基本的概念就是實作一個類別,將 *-> 這兩個運算子多載化(overloading),並在解構子(destructor)中加入記憶體回收的功能。

#include<iostream>
class Ptr {  // 實作智慧型指標的類別
  int *ptr;
  public:
  explicit Ptr(int *p = NULL) { ptr = p; }  // 將指標儲存起來
  ~Ptr() { delete(ptr); }  // 回收記憶體
  int &operator *() {  return *ptr; }  // 多載化 * 運算子
};
int main() {
  Ptr ptr(new int());
  *ptr = 4;
  cout << *ptr;
  // 智慧型指標會自動回收記憶體
  return 0;
}

這裏我們使用智慧型指標來管理記憶體,在動態配置的記憶體使用完之後,它就會自動回收沒有用的記憶體。這裡我們以基本的 int* 指標來示範實作的概念,實務上通常我們會改用泛型程式設計的方式,實作各種型別都可以用的智慧型指標。

由於智慧型指標已經是一個很普遍的技術了,有許多開放原始碼的函式庫都有提供這樣的功能,與其自己寫倒不如直接使用既有的函式庫,例如 boost 就有提供這樣的功能,另外在 C++ 11 的標準中也加入了 General-purpose smart pointers,所以如果現在要開發新的程式,直接使用這些資源會比較輕鬆。

參考資料:The Geek Stuff