a review at smart pointer(4)
阿新 • • 發佈:2021-01-09
#include "stdafx.h" #include <iostream> #include <future> #include <thread> using namespace std; class Person { public: Person(int v) { value = v; std::cout << "Cons" <<value<< std::endl; } ~Person() { std::cout << "Des" <<value<< std::endl; } int value; }; int main() { std::shared_ptr<Person> p1(new Person(1));// Person(1)的引用計數為1 std::shared_ptr<Person> p2 = std::make_shared<Person>(2); p1.reset(new Person(3));// 首先生成新物件,然後引用計數減1,引用計數為0,故析構Person(1) // 最後將新物件的指標交給智慧指標 std::shared_ptr<Person> p3 = p1;//現在p1和p3同時指向Person(3),Person(3)的引用計數為2 p1.reset();//Person(3)的引用計數為1 p3.reset();//Person(3)的引用計數為0,析構Person(3) return 0; }
- 首先我們解釋一下什麼是引用計數,首先初始化一個shared ptr p1,然後其中的成員是new Person(1)
- 記憶體佈局可以理解為,一個heap區域的person類其地址為(假設為hp1),然後一個shared ptr在stack區,叫做p1
- 然後同樣的:heap區域的person類person(2)和其地址為hp2,然後一個shared ptr在stack區:p2
- p1 reset一個new person(3), 這個的解釋過程為:首先在heap區new一個person 3,其地址為hp3,然後,執行reset函式,因為reset的目的永遠是釋放一個count,所以count - 1變成0,然後析構person(1),所以,hp1消失,然後hp3交給p1,count自增
- 然後複製一個shared ptr p3,值是p1,裡面的內容也是一樣的, 我們發現hp3既交給了p1也交給了p3,count為2
- p1.reset, count--
- p3.reset, count--
所以我們可以發現,share ptr的share為什麼是share? 它代表的是多個share指標共享一個堆資源
之後我有個碩大的疑問
- 來源某pp prime的一句話:shared_ptr自動銷燬所管理的物件
當指向一個物件的最後一個shared_ptr被銷燬時,shared_ptr類會自動銷燬此物件,它是通過另一個特殊的成員函式-解構函式完成銷燬工作的,類似於建構函式,每個類都有一個解構函式。解構函式控制物件銷燬時做什麼操作。解構函式一般用來釋放物件所分配的資源。shared_ptr的解構函式會遞減它所指向的物件的引用計數。如果引用計數變為0,shared_ptr的解構函式就會銷燬物件,並釋放它所佔用的記憶體。 - 然後不僅僅是這一個部落格上有說:不要用一個原始指標初始化多個shared_ptr,原因在於,會造成二次銷燬
int *p5 = new int;
std::shared_ptr<int> p6(p5);
std::shared_ptr<int> p7(p5);// logic error
- 上述程式碼很好理解,二次銷燬誰不知道
我的問題是,這篇文章的第一份程式碼不也是兩個指標指向一個資源嗎?為什麼這個就沒有錯呢?
答案是:第一份程式碼是兩個智慧指標有著一模一樣的值,它是shared ptr(語義上),指向一個資源,它們在stack區域的值是一模一樣的,=之間應該是有運算子過載的機制讓count自增(代表了shared,所以count自增)
但是第二個例子不對的地方在於,它並不是智慧指標的賦值,它是讓一個指標進入兩個建構函式,所以count應該是無自增的機制的
第一個例子由shared ptr自己的機制進行垃圾回收,根據count何時減到0
- 另外,不要在函式實參中建立shared_ptr
// 錯
function(shared_ptr<int>(new int), g());
通過查閱文件我們發現unique ptr的預設刪除器是支援釋放陣列物件的,如: std::unique_ptr<int[]> foo (new int[5]); 但是shared ptr 不支援[] ,所以我們要自定義deleter
關於到底是int[]還是int,自定義(不自定義)deleter的問題,我直說了: 我看不懂,因為20,17,11三個版本的要求全不一樣,感興趣的參見這篇部落格 https://www.cnblogs.com/apocelipes/p/10346928.html 到時候開發就現查文件
- 另外,注意unique ptr可以轉shared ptr,反過來不可,比如
#include <iostream>
#include <memory>
using namespace std;
class A{
public:
string id;
A(string id):id(id){cout<<id<<":建構函式"<<endl;}
~A(){cout<<id<<":解構函式"<<endl;}
};
int main() {
unique_ptr<A> a(new A("unique_ptr"));
shared_ptr<A> b = move(a);
// a = move(b); // 報錯
// a.reset(b.get()); // 執行錯誤
cout<<a.get()<<endl;
return 0;
}
-
用get拿到智慧指標的裸指標之後刪掉它,會導致智慧指標執行錯誤
-
不要用stack中的變數地址初始化一個smart pointer
#include <iostream>
#include <memory>
using namespace std;
class A{
public:
string id;
A(string id):id(id){cout<<id<<":建構函式"<<endl;}
~A(){cout<<id<<":解構函式"<<endl;}
};
A a("全域性變數");
int main() {
A b("區域性變數");
// unique_ptr<A> pa(&a); // 執行錯誤
unique_ptr<A> pa(&b);
return 0;
}
謹慎使用智慧指標的get與release方法
- 通過unique_ptr.release()方法返回的裸指標,需要我們自己delete刪除物件,因為呼叫release方法後,該unique_ptr不再擁有物件的所有權。