c++ shared_ptr的使用
shared_ptr.是c++為了提高指針安全性而添加的智能指針,方便了內(nèi)存管理。功能非常強大,非常強大,非常強大(不單單是shared_ptr,配合week_ptr以及enable_share_from_this()以及share_from_this())!!!對于支持智能指針的c++版本編程,能用智能指針就用智能指針!
shared_ptr是一種智能指針(smart pointer),作用有如同指針,但會記錄有多少個shared_ptrs共同指向一個對象。這便是所謂的引用計數(shù)(reference counting)。一旦最后一個這樣的指針被銷毀,也就是一旦某個對象的引用計數(shù)變?yōu)?,這個對象會被自動刪除。這在非環(huán)形數(shù)據(jù)結(jié)構(gòu)中防止資源泄露很有幫助。使得指針可以共享對象,并且不用考慮內(nèi)存泄漏問題 shared_ptr 可以支持普通指針的所有操作,完全可以像操作普通指針一樣操作智能指針。 shared_ptr 可以通過三種方式得到(拷貝初始化,定義delete操作的方式不在羅列,只討論初始化指針?biāo)笇ο髞碓?#xff09;: 1.通過一個指向堆上申請的空間的指針初始化(切記不要用棧上的指針,否則,當(dāng)智能指針全部釋放控制權(quán)(棧中的對象離開作用域本身就會析構(gòu)一次),將會析構(gòu)對象,導(dǎo)致出錯) 2.通過make_shared函數(shù)得到 3.通過另外一個智能指針初始化 #include <memory> #include <iostream> int main() { int *p = new int(30); std::shared_ptr<int> bptr(p);//方式1 std::shared_ptr<int> aptr = std::make_shared<int>(20);//方式 std::shared_ptr<int> cptr(aptr); std::cout << "aptr.use_count() = " << aptr.use_count() <<" value = "<<*aptr<<std::endl;//use_count 是引用計數(shù)器 std::cout << "bptr.use_count() = " << bptr.use_count() <<" value = "<<*bptr<<std::endl; std::cout << "cptr.use_count() = " << cptr.use_count() <<" value = "<<*cptr<<std::endl; //輸出是:2,20 // 1,30 // 2,20 }下面是使用shared_ptr 的一些注意事項:
0. 禁止純指針給智能指針賦值或者拷貝構(gòu)造。
int* a=new int(2); shared_ptr<int>sp=a;// error sp=a;// error
1. shared_ptr多次引用同一數(shù)據(jù),會導(dǎo)致兩次釋放同一內(nèi)存。如下:
{
int* pInt = new int[100];
shared_ptr<int> sp1(pInt);
// 一些其它代碼之后…
shared_ptr<int> sp2(pInt);
}
2.使用shared_ptr包裝this指針帶來的問題,如下:
class tester?
{
public:
? tester()
? ~tester()
? {
??? std::cout << "析構(gòu)函數(shù)被調(diào)用!\n";?
? }
public:
? shared_ptr<tester> sget()
? {
??? return shared_ptr<tester>(this);
? }
};
int main()
{
? tester t;
? shared_ptr<tester> sp =? t.sget(); // …
? return 0;
}
也將導(dǎo)致兩次釋放t對象破壞堆棧,一次是出棧時析構(gòu),一次就是shared_ptr析構(gòu)。若有這種需要,可以使用下面代碼。
class tester : public enable_shared_from_this<tester>
{
public:
? tester()
? ~tester()
? {
? std::cout << "析構(gòu)函數(shù)被調(diào)用!\n";?
? }
public:
?shared_ptr<tester> sget()
? {
? return shared_from_this();
? }
};
int main()
{
? shared_ptr<tester> sp(new tester);
? // 正確使用sp 指針。
? sp->sget();
? return 0;
}
3. shared_ptr循環(huán)引用導(dǎo)致內(nèi)存泄露,代碼如下:
class parent;
class child;?
typedef shared_ptr<parent> parent_ptr;
typedef shared_ptr<child> child_ptr;?
class parent
{
public:
?????? ~parent() {?
????????????? std::cout <<"父類析構(gòu)函數(shù)被調(diào)用.\n";?
?????? }
public:
?????? child_ptr children;
};
class child
{
public:
?????? ~child() {?
????????????? std::cout <<"子類析構(gòu)函數(shù)被調(diào)用.\n";?
?????? }
public:
?????? parent_ptr parent;
};
int main()
{
? parent_ptr father(new parent());
? child_ptr son(new child);
? // 父子互相引用。
? father->children = son;
? son->parent = father;
? return 0;
}
如上代碼,將在程序退出前,father的引用計數(shù)為2,son的計數(shù)也為2,退出時,shared_ptr所作操作就是簡單的將計數(shù)減1,如果為0則釋放,顯然,這個情況下,引用計數(shù)不為0,于是造成father和son所指向的內(nèi)存得不到釋放,導(dǎo)致內(nèi)存泄露。?
4.在多線程程序中使用shared_ptr應(yīng)注意的問題。代碼如下:
class tester?
{
public:
? tester() {}
? ~tester() {}
? // 更多的函數(shù)定義…
};
void fun(shared_ptr<tester> sp)
{
? // !!!在這大量使用sp指針.
? shared_ptr<tester> tmp = sp;
}
int main()
{
? shared_ptr<tester> sp1(new tester);
? // 開啟兩個線程,并將智能指針傳入使用。
? thread t1(bind(&fun, sp1));
? thread t2(bind(&fun, sp1));
? t1.join();
? t2.join();
? return 0;
}
這個代碼帶來的問題很顯然,由于多線程同時訪問智能指針,并將其賦值到其它同類智能指針時,很可能發(fā)生兩個線程同時在操作引用計數(shù)(但并不一定絕對發(fā)生),而導(dǎo)致計數(shù)失敗或無效等情況,從而導(dǎo)致程序崩潰,如若不知根源,就無法查找這個bug,那就只能向上帝祈禱程序能正常運行。
引入weak_ptr可以解決這個問題,將fun函數(shù)修改如下:
void fun(weak_ptr<tester> wp)
{
//這個方案只解決了多線程對引用計數(shù)同時訪問的讀寫問題,并沒有解決對share_ptr指向的數(shù)據(jù)的多線程安全問題,因此weak_ptr只是安全的獲得share_ptr的一種方式,因為可以確保在獲得share_ptr的時候的多線程安全
? shared_ptr<tester> sp = wp.lock;
? if (sp)
? {
??? // 在這里可以安全的使用sp指針.
? }
? else
? {
??? std::cout << “指針已被釋放!” << std::endl;
? }
}?
5.weak_ptr不僅可以解決多線程訪問帶來的安全問題,而且還可以解決上面第三個問題循環(huán)引用。Children類代碼修改如下,即可打破循環(huán)引用:
class child
{
public:
? ~child() {?
?? std::cout <<"子類析構(gòu)函數(shù)被調(diào)用.\n";?
? }
public:
? weak_ptr<parent> parent;
};
因為weak_ptr不增加引用計數(shù),所以可以在退出函數(shù)域時,正確的析構(gòu)。
weak_ptr 介紹:
weak_ptr是為了配合shared_ptr而引入的一種智能指針,它更像是shared_ptr的一個助手而不是智能指針,因為它不具有普通指針的行為,沒有重載operator*和->,它的最大作用在于協(xié)助shared_ptr工作,像旁觀者那樣觀測資源的使用情況.
用法:
?weak_ptr被設(shè)計為與shared_ptr共同工作,可以從一個shared_ptr或者另一個weak_ptr對象構(gòu)造,獲得資源的觀測權(quán)。但weak_ptr沒有共享資源,它的構(gòu)造不會引起指針引用計數(shù)的增加。
?使用weak_ptr的成員函數(shù)use_count()可以觀測資源的引用計數(shù),另一個成員函數(shù)expired()的功能等價于use_count()==0,但更快,表示被觀測的資源(也就是shared_ptr的管理的資源)已經(jīng)不復(fù)存在。
?weak_ptr可以使用一個非常重要的成員函數(shù)lock()從被觀測的shared_ptr獲得一個可用的shared_ptr對象, 從而操作資源。但當(dāng)expired()==true的時候,lock()函數(shù)將返回一個存儲空指針的shared_ptr.
share_ptr 的 aliasing constructor:
可以使得share_ptr 擁有某個對象控制權(quán)的時候,存儲指針(storage pointer,shared_ptr 還有一個概念叫owned ptr 指向存儲當(dāng)前控制對象相關(guān)信息)指向另外一個對象,模板如下:
template <class U> shared_ptr (const shared_ptr<U>& x, element_type* p) noexcept;? 該構(gòu)造函數(shù)是的shared_ptr 管理 x 指針指向?qū)ο?#xff0c;會將計數(shù)器加一,并且在釋放控制權(quán)時減一,如有需要還會調(diào)用析構(gòu)函數(shù),但是不同的是它storage ptr 指向的是p,調(diào)用 share_ptr.get() 會返回 p ,一般的用途在 p所指對象是x所指對象的成員或者別名(alias)[這段是翻譯的,總之,aliasing constructor 還是慎用吧]
?本文轉(zhuǎn)載于:https://blog.csdn.net/man_sion/article/details/77196766
轉(zhuǎn)載于:https://www.cnblogs.com/curo0119/p/8919990.html
總結(jié)
以上是生活随笔為你收集整理的c++ shared_ptr的使用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Git--团队开发必备神器
- 下一篇: s3c2440移植MQTT