先看一個例子:Stark和Targaryen家族你中有我,我中有你。我們設計以下類企圖避免內存泄漏,使得析構函數都能調用到:
#include<iostream> #include<memory> using namespace std; class Stark; class Targaryen; class Stark { private: Targaryen *targaryen; public: void prin(){cout<<"stark love targaryen"<<endl;} ~Stark() { delete targaryen; cout<<"~Stark"<<endl; } }; class Targaryen { private: Stark *stark; public: void prin() { cout << "targaryen love stark" << endl; } ~Targaryen() { delete stark; cout << "~Targaryen" << endl; } }; int main() { Targaryen *tar = new Targaryen; Stark *stark = new Stark; delete stark; system("pause"); return 0; }
打印結果:
正常來說,我們要求的結果是兩個對象都要析構掉,但是我們可以debug執行看到,並沒有全部析構,顯然不是我們的需求!
那么換一種智能指針的寫法,看看結果怎么樣:
class Stark; class Targaryen; class Stark { private: //Targaryen *targaryen; shared_ptr<Targaryen> share_tar; public: void prin(){cout<<"stark love targaryen"<<endl;} void setTargaryen(shared_ptr<Targaryen> tar) { this->share_tar = tar; } ~Stark() { //delete targaryen; cout<<"~Stark"<<endl; } }; class Targaryen { private: //Stark *stark; shared_ptr<Stark> share_stark; //weak_ptr<Stark> weak_stark; public: void prin() { cout << "targaryen love stark" << endl; } void setStark(shared_ptr<Stark> stark) { this->share_stark = stark; } ~Targaryen() { //delete stark; cout << "~Targaryen" << endl; } }; int main() { weak_ptr<Stark> wpstark; weak_ptr<Targaryen> wptar; //Targaryen *tar = new Targaryen; //Stark *stark = new Stark; { shared_ptr<Targaryen> tar(new Targaryen); shared_ptr<Stark> stark(new Stark); tar->prin(); stark->prin(); tar->setStark(stark); stark->setTargaryen(tar); wpstark = stark; wptar = tar; cout << tar.use_count() << endl; cout << stark.use_count() << endl; } cout << wpstark.use_count() << endl; cout << wptar.use_count() << endl; //delete stark; system("pause"); return 0; }
我們希望在main中第一對{}號結束的時候,打印析構函數,但是並沒有
那我們再換一種寫法:
class Stark; class Targaryen; class Stark { private: //Targaryen *targaryen; shared_ptr<Targaryen> share_tar; public: void prin(){cout<<"stark love targaryen"<<endl;} void setTargaryen(shared_ptr<Targaryen> tar) { this->share_tar = tar; } ~Stark() { //delete targaryen; cout<<"~Stark"<<endl; } }; class Targaryen { private: //Stark *stark; //shared_ptr<Stark> share_stark; ////這里變了~~~~~~~~~~~~ weak_ptr<Stark> weak_stark; public: void prin() { cout << "targaryen love stark" << endl; } void setStark(shared_ptr<Stark> stark) { this->weak_stark = stark; } ~Targaryen() { //delete stark; cout << "~Targaryen" << endl; } };
這次結果還是令人滿意的。
那么問題來了,為什么要這么做呢?為什么要用weak_ptr取代shared_ptr呢?
我們看weak_ptr的官方定義:
std::weak_ptr
是一種智能指針,它對被 std::shared_ptr 管理的對象存在非擁有性(“弱”)引用。在訪問所引用的對象前必須先轉換為 std::shared_ptr。
std::weak_ptr
用來表達臨時所有權的概念:當某個對象只有存在時才需要被訪問,而且隨時可能被他人刪除時,可以使用 std::weak_ptr
來跟蹤該對象。需要獲得臨時所有權時,則將其轉換為 std::shared_ptr,此時如果原來的 std::shared_ptr 被銷毀,則該對象的生命期將被延長至這個臨時的 std::shared_ptr 同樣被銷毀為止。
std::weak_ptr 的另一用法是打斷 std::shared_ptr 所管理的對象組成的環狀引用。若這種環被孤立(例如無指向環中的外部共享指針),則 shared_ptr
引用計數無法抵達零,而內存被泄露。能令環中的指針之一為弱指針以避免此情況。
這種就是一種環狀的情況。
另外,還有一點要注意:
1 int main() 2 { 3 { 4 int *a = new int; 5 std::shared_ptr<int> p1(a); 6 std::shared_ptr<int> p2(a); 7 } 8 system("pause"); 9 return 0; 10 }
這樣寫會報錯。因為,析構的時候,指針a會被delete兩次。因此,為了避免這種情況的發生,我們盡可能不適用new來初始化shared_ptr。而是用make_shared;
1 class Mars 2 { 3 public: 4 Mars () 5 { 6 cout << this << ": Mars" << endl; 7 } 8 ~Mars() 9 { 10 cout << this << ": ~Mars" << endl; 11 } 12 }; 13 int main() 14 { 15 { 16 Mars pMars; 17 shared_ptr<Mars> p1 = make_shared<Mars >(pMars); 18 shared_ptr<Mars> p2 = make_shared<Mars >(pMars); 19 } 20 system("pause"); 21 return 0; 22 }
這玩意兒太復雜了~只是清楚大概是干什么的。但是還不會用……以及什么時候用,關鍵是我們這公司平時也不用,這就尷尬了。
用shared_ptr,不用new
使用weak_ptr來打破循環引用
用make_shared來生成shared_ptr
用enable_shared_from_this來使一個類能獲取自身的shared_ptr
結束!以后有時間再慢慢研究。
大部分都是抄的:
https://zh.cppreference.com/w/cpp/memory/shared_ptr
https://www.cnblogs.com/wxquare/p/4759020.html
https://blog.csdn.net/worldwindjp/article/details/18843087
https://heleifz.github.io/14696398760857.html