C++ 智慧指標(一)
-
記憶體安全
在C++中,動態記憶體的管理是通過一對運算子來完成的:new,在動態記憶體中為物件分配空間並返回一個指向該物件的指標,我們可以選擇對物件來進行初始化;delete,接收一個動態物件的指標,銷燬該物件,並釋放與之關聯的記憶體。
動態記憶體的使用很容易出問題,因為確保在正確的時間釋放記憶體是及其困難的。有時我們會忘記釋放記憶體(或程式丟擲異常),在這種情況下就會產生記憶體洩漏;有時在尚有指標引用記憶體的情況下我們就釋放了它,在這種情況下就會產生引用非法記憶體的指標(段錯誤)。
下面寫個Demo測試程式
#include <memory> #include <exception> #define FLAG 3 //用於編譯不同的程式 class Demo1 { public: Demo1() { std::cout << "Demo1" << std::endl; } ~Demo1() { std::cout << "~Demo1" << std::endl; } }; bool throw_test(bool flag) { if (flag) { throw "throw_test"; } return flag; } int main(int argc, char* argv[]) { Demo1 *pDemo1 = new Demo1(); #if (FLAG == 1) throw_test(true); //1.執行這條語句,會列印~Demo1? #endif #if (FLAG == 2) try { throw_test(true); } catch (...) //2....代表捕獲所有異常 { delete pDemo1; //3.執行這條語句,會列印~Demo1? throw; } #endif #if (FLAG == 3) delete pDemo1; #endif return 0; }
上述程式結果如下:
當巨集FLAG為1時,執行throw_test(true),即使程式丟擲異常,它沒有列印~Demo1;
當巨集FLAG為2時,執行throw_test(true),程式會丟擲異常,之後捕獲到異常列印~Demo1;
當巨集FLAG為3時,執行delete pDemo1,這是正常操作,程式會呼叫Demo1的解構函式,列印~Demo1;
-
智慧指標
C++中的智慧指標型別有:auto_ptr,shared_ptr,unique_ptr,weak_ptr(後三者為C++11新增的),它們均為類模板,使用需要包含<memory>標頭檔案
常規指標帶來的風險
Demo1* pDemo1 = new Demo1(); Demo1* pDemo2; pDemo2 = pDemo1; printf("pDemo1:%x pDemo2:%x\n", pDemo1, pDemo2);
上面的pDemo1和pDemo2是常規指標,指向同一個物件(淺拷貝),因此列印的地址也是一樣的;請試想一下如果其中一個指標執行了delete操作,那麼另一個指標再執行別的操作會怎樣?程式會發生段錯誤。要避免這個問題,可以用下面這些方案:
- 定義賦值運算子函式,進行深拷貝,這樣的操作會使上面的兩個指標不再指向同一個物件,缺點是浪費空間,所以智慧指標都未採用此方案
- "獨佔"所指的物件;對於特定的物件,某一時刻只能有一個指標指定一個給定物件,當指標被銷燬時,它所指的物件也被銷燬,這就是用於auto_ptr和uniqiie_ptr 的策略,但unique_ptr的策略更嚴格。
- 利用引用計數,建立記錄型的指標;例如,賦值時,計數將加1,而指標過期時,計數將減1。當減為0時才呼叫delete。這是shared_ptr採用的策略。
在C++11中,auto_ptr已棄用;編寫一段測試程式,Demo1類還是使用上面定義的。
int main(int argc, char *argv[]) { std::auto_ptr<Demo1> pDemo1(new Demo1); std::auto_ptr<Demo1> pDemo2; pDemo2 = pDemo1; printf("pDemo1:%p pDemo2:%p\n", pDemo1, pDemo2); //執行到這裡pDemo1會列印什麼? return 0; }
上面的程式執行後,列印pDemo1的地址為NULL。具體可以檢視下auto_ptr的賦值運算子的實現是如何的。
下面這兩張截圖是VS2015下的auto_ptr的賦值運算子的實現;我們可以看到使用賦值運算子時,會呼叫reset函式,這是會將_Myptr的記憶體delete,並將地址置為NULL;如果pDemo1呼叫成員函式,此時會發送什麼?
auto_ptr存在記憶體崩潰的風險,這個或許就是auto_ptr被C++11棄用的原因吧。
未完待續...