一:背景
我們知道 C++ 是手工管理記憶體的分配和釋放,對應的運算子就是 new/delete 和 new[] / delete[], 這給了程式員極大的自由度也給了我們極高的門檻,弄不好就得記憶體泄露,比如下面的代碼:
void test() {
int* i = new int(10);
*i = 10;
}
int main() {
test();
}
這段代碼因為用了 new 而忘了 delete,導致在 nt heap 上分配的 i 隨著堆疊地址的回收而成了一塊孤懸海外的記憶體占用,所以修正后的代碼如下:
void test() {
int* i = new int(10);
*i = 10;
delete i;
}
int main() {
test();
}
但這種寫法比較麻煩,智者千慮必有一失,總會有忘記加 delete 的時候,那怎么辦呢? 大家應該知道記憶體自動管理有兩種手段,
- 參考計數
代表作有 Python,PHP,還有 windows 的句柄管理,
- 參考跟蹤
代表作有 C#,JAVA 等一眾工程化語言,
因為 參考計數 實作比較簡單,主要就是記錄下物件的參考次數,次數為 0 則釋放,所以可完全借助 類的建構式解構式 和 堆疊的自動回收特性 弄一個簡單的 參考計數 ,對應著如下四個關鍵詞,
-
auto_ptr
-
shared_ptr
-
unique_ptr
-
weak_ptr
接下來我們逐個聊一聊,
二:關鍵詞決議
1. auto_ptr
這是 C++ 最早出現一個的 簡單參考計數法,參考代碼如下:
void test() {
auto_ptr<int> ptr = auto_ptr<int>(new int(10));
}
int main() {
test();
}
接下來看下匯編代碼:
auto_ptr<int> ptr = auto_ptr<int>(new int(10));
...
00771D26 call std::auto_ptr<int>::auto_ptr<int> (07710FAh)
00771D2B lea ecx,[ebp-0D8h]
00771D31 call std::auto_ptr<int>::~auto_ptr<int> (0771159h)
可以看到,它分別呼叫了 建構式 和 解構式,接下來找下 auto_ptr 這兩個函式的原始碼,
class auto_ptr {
private:
_Ty* _Myptr; // the wrapped object pointer
public:
auto_ptr(auto_ptr_ref<_Ty> _Right) noexcept {
_Ty* _Ptr = _Right._Ref;
_Right._Ref = nullptr; // release old
_Myptr = _Ptr; // reset this
}
~auto_ptr() noexcept {
delete _Myptr;
}
}
原始碼一看就明白了,在建構式中,將 new int 的地址塞給了內部的 _Myptr 指標,在解構式中對 _Myptr 進行 delete ,真好,這樣就不用整天擔心有沒有加 delete 啦,
值得注意的是,現在 C++ 不推薦這個了,而是建議使用新增的:shared_ptr,unique_ptr,weak_ptr, 怎么說呢? auto_ptr 有一個不好處理的問題,就是現實開發中會出現這么個場景,多個 ptr 指向同一個 參考,如下圖:
2. auto_ptr 多參考問題
- 方式1:
定義三個 ptr,然后包裝同一個 new int 地址,參考代碼如下:
void test() {
int* i = new int(10);
auto_ptr<int> ptr1(i);
auto_ptr<int> ptr2(i);
auto_ptr<int> ptr3(i);
}
這種寫法有沒有問題呢? 肯定有問題啦,還記得 auto_ptr 的析構是 delete 嗎? 對同一塊記憶體多次 delete 會拋例外的,如下圖所示:
- 方式2:
既然定義三個有問題, 那就用賦值運算子= 讓 ptr1,ptr2,ptr3 指向同一個地址是不是就可以啦? 參考代碼如下:
void test() {
int* i = new int(10);
auto_ptr<int> ptr1(i);
auto_ptr<int> ptr2 = ptr1;
auto_ptr<int> ptr3 = ptr2;
}
int main() {
test();
}
那這段代碼有沒有問題呢? 有沒有問題得要看 = 運算子是如何重寫的??,扒一下原始碼看看,
template <class _Other>
auto_ptr& operator=(auto_ptr<_Other>& _Right) noexcept {
reset(_Right.release());
return *this;
}
_Ty* release() noexcept {
_Ty* _Tmp = _Myptr;
_Myptr = nullptr;
return _Tmp;
}
從原始碼看有一個很惡心的點,他會將 _Right 下的 _Myptr 設為 nullptr,也就是說此時的 ptr1 報廢了,言外之意就是后續再訪問 ptr1 會拋 訪問違例,
哈哈,C++里面的專業術語叫 控制權轉移,
好了,本篇就說這么多吧,下一篇聊聊新增的這些關鍵詞,看看如何將 auto_ptr 更合理的分權,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/499599.html
標籤:C++
下一篇:第2章 開始學習C++
