物聯(lián)網(wǎng)/嵌入式工程師
重磅升級:新增硬件電路設(shè)計與實戰(zhàn),讓你軟硬通吃,同級PK無敵!行業(yè)風(fēng)口、政策傾斜,新晉熱門高薪不內(nèi)卷!0基礎(chǔ)一站式就業(yè)完整路徑,搶占先發(fā)優(yōu)勢!
在之前的課程中,我們講過使用 new 關(guān)鍵字將一個對象分配到堆上,分配到堆上的對象必須手動 delete 掉,否則就會造成內(nèi)存泄漏的問題。我們常常因為忘記釋放內(nèi)存,或者釋放內(nèi)存的時機(jī)不對而出現(xiàn)問題。
為了可以讓指針自行釋放,在 C++11 標(biāo)準(zhǔn)中,加入了一種可以自行釋放的指針,叫做智能指針。
我們首先來介紹第一種智能指針:std::unique_ptr。
std::unique_ptr 用于不能被多個實例共享的內(nèi)存管理。它將普通的指針封裝為一個棧對象,我們之前學(xué)過棧對象的生命周期,當(dāng)棧對象的生存周期結(jié)束后,會在析構(gòu)函數(shù)中釋放掉申請的內(nèi)存,從而防止內(nèi)存泄漏。這就是說,僅有一個實例擁有內(nèi)存所有權(quán)。我們可以通過一個實例來看一下
#include <memory> class A{ public: A() { } ~A() { } };
int main(int argc,char **argv) { std::unique_ptr<A> p1(new A()); return 0; }
在上面的代碼中,我們初始化了一個智能指針,指向了一片 A 對象的內(nèi)存。要注意的是 unique_ptr 只能通過構(gòu)造函數(shù)將指針傳入,而不能將一個指針直接進(jìn)行賦值。也就是說,這句話不能寫成:
std::unique_ptr<A> p1 = new A(); // 會報錯
在初始化之后,就可以像普通指針那樣操作這個智能指針,不一樣的是,在使用完之后,不用去 delete 他,他會自動釋放內(nèi)存。
值得注意的是,unique_ptr 只能在初始化的時候指向一片內(nèi)存,之后他就不能被別人重新賦值,也不能賦值給其他指針,這是一個孤獨的智能指針。
想讓指針不那么孤獨,我們可以使用 shared_ptr。
std::shared_ptr 與 std::unique_ptr 的主要區(qū)別在于前者是使用引用計數(shù)的智能指針。引用計數(shù)的智能指針可以跟蹤引用同一個真實指針對象的智能指針實例的數(shù)目。在這種智能指針中,有一個整型變量,被稱之為引用計數(shù)變量。當(dāng)這個指針發(fā)生賦值到其他指針的操作的時候,說明這個智能指針指向的對象被別人“分享”了一次,則引用計數(shù)加1;而當(dāng)一個智能指針被銷毀,就是這個智能指針的析構(gòu)函數(shù)被調(diào)用的時候,說明有一個人對其放棄了“分享”,則引用計數(shù)減1,當(dāng)引用計數(shù)變成 0 的時候,說明這個對象將不被任何指針指向,這時候,就可以銷毀這個對象了。
shared_ptr 的用法如下:
int main(int argc,char **argv)
{
std::shared_ptr<A> p1 = std::make_shared<A>();
std::shared_ptr<A> p2 = p1;
return 0;
}
在上述例子中 p1 和 p2 指向了同一片內(nèi)存,兩個指針都能操作這片內(nèi)存,而且還不用釋放。
值得注意的是,上面的例子中,我們沒有使用 new ,而是使用了 make_shared 來構(gòu)建對象。make_shared 可以保留指針的關(guān)系,避免錯誤發(fā)生,也是 shared_ptr 推薦的方式。
std::shared_ptr 看起來能解決絕大多數(shù)的問題,但是,std::shared_ptr 在有一個場景下會發(fā)生錯誤,那就是循環(huán)引用。
我們來看這樣一個程序
class A{
public:
A()
{
printf("A()\n");
}
~A()
{
printf("~A()\n");
}
int aaa(){
return 0;
}
std::shared_ptr<A> a;
};
int main(int argc,char **argv)
{
std::shared_ptr<A> p1 = std::make_shared<A>();
std::shared_ptr<A> p2 = std::make_shared<A>();
p1->a = p2;
p2->a = p1;
return 0;
}
在這個程序中,我們給 A 類也添加了兩個智能指針的成員變量。然后在 main 函數(shù)中,讓他們相互引用。正常來說,我們 make_shared 了兩次,應(yīng)該調(diào)用了兩次 A 的構(gòu)造函數(shù),之后又會調(diào)用兩次 A 的析構(gòu)函數(shù)。但是運行這個程序,就會發(fā)現(xiàn),沒有析構(gòu)函數(shù)被調(diào)用,也就是說,兩個指針都沒有被銷毀。這是因為 p1 和 p2 是一個相互引用的狀態(tài),這種相互引用的狀態(tài)會導(dǎo)致一種“死鎖”現(xiàn)象的產(chǎn)生,最終導(dǎo)致兩者都無法釋放。
為了解決這個問題,C++ 提供了 weak_ptr。在相互引用的時候,使用 weak_ptr 就可以防止死鎖。
class A{
public:
A()
{
printf("A()\n");
}
~A()
{
printf("~A()\n");
}
int aaa(){
return 0;
}
std::weak_ptr<A> a;
};
請驗證,完成請求
由于請求次數(shù)過多,請先驗證,完成再次請求
打開微信掃碼自動綁定
綁定后可得到
使用 Ctrl+D 可將課程添加到書簽
舉報