(六)boost库之内存管理shared_ptr
1、shared_ptr的基本用法
boost::shared_ptr<int> sp(new int(10)); //一個指向整數的shared_ptr assert(sp.unique()); //現在shared_ptr是指針的唯一持有者 boost::shared_ptr<int> sp2 = sp; //第二個shared_ptr,拷貝構造函數 assert(sp == sp2 && sp.use_count() == 2); //兩個shared_ptr相等,指向同一個對象,引用計數為2 *sp2 = 100; //使用解引用操作符修改被指對象 assert(*sp == 100); //另一個shared_ptr也同時被修改 sp.reset(); //停止shared_ptr的使用,引用計數減一 assert(!sp); //sp不再持有任何指針(空指針) assert(sp2.use_count() == 1); //sp2引用計數變為1 sp.reset(new int(20)); //sp管理一個新對象 assert(*sp == 20);?
2、應用于標準容器
??? 有兩種方式可以將shared_ptr應用于標準容器(或者容器適配器等其他容器)。
??? 一種用法是將容器作為shared_ptr管理的對象,如shared_ptr<list<T> >,使容器可以被安全地共享,用法與普通shared_ptr沒有區別,我們不再討論。
??? 另一種用法是將shared_ptr作為容器的元素,如vector<shared_ptr<T> >,因為shared_ptr支持拷貝語義和比較操作,符合標準容器對元素的要求,所以可以實現在容器中安全地容納元素的指針而不是拷貝。
??? 標準容器不能容納auto_ptr,這是C++標準特別規定的(讀者永遠也不要有這種想法)。標準容器也不能容納scoped_ptr,因為scoped_ptr不能拷貝和賦值。標準容器可以容納原始指針,但這就喪失了容器的許多好處,因為標準容器無法自動管理類型為指針的元素,必須編寫額外的大量代碼來保證指針最終被正確刪除,這通常很麻煩很難實現。
??? 存儲shared_ptr的容器與存儲原始指針的容器功能幾乎一樣,但shared_ptr為程序員做了指針的管理工作,可以任意使用shared_ptr而不用擔心資源泄漏。
#include <boost/make_shared.hpp> int main() { typedef vector<shared_ptr<int> > vs; //一個持有shared_ptr的標準容器類型 vs v(10); //聲明一個擁有10個元素的容器,元素被初始化為空指針 int i = 0; for (vs::iterator pos = v.begin(); pos != v.end(); ++pos) { (*pos) = make_shared<int>(++i); //使用工廠函數賦值 cout << *(*pos) << ", "; //輸出值 } cout << endl; shared_ptr<int> p = v[9]; *p = 100; cout << *v[9] << endl; }??? 這段代碼需要注意的是迭代器和operator[]的用法,因為容器內存儲的是shared_ptr,我們必須對迭代器pos使用一次解引用操作符*以獲得shared_ptr,然后再對shared_ptr使用解引用操作符*才能操作真正的值。*(*pos)也可以直接寫成**pos,但前者更清晰,后者很容易讓人迷惑。vector的operator[]用法與迭代器類似,也需要使用*獲取真正的值。
3、使用助手類enable_shared_from_this
??? 為什么要使用enable_shared_from_this,或許你對這個類感到很迷惑,先看看下面這種情況:
class MyPoint { public: MyPoint(){std::cout << "MyPoint" << std::endl;} ~MyPoint(){std::cout << "~MyPoint" << std::endl;} //返回this的函數 boost::shared_ptr<MyPoint> GetPoint() { return boost::shared_ptr<MyPoint>(this); //錯誤,將返回一個新的引用計數 } }; int _tmain(int argc, _TCHAR* argv[]) { boost::shared_ptr<MyPoint> p1(new MyPoint); boost::shared_ptr<MyPoint> p2 = p1->GetPoint(); std::cout << p1.use_count() << "," << p2.use_count() << std::endl; //輸出引用計數情況 p1.reset(); //內存將被釋放 }我們得到的答案將是:
 MyPoint?
 1,1?
 ~MyPoint
怎么正確的返回this呢,那么就需要借助enable_shared_from_this了,引入enable_shared_from_this的原因是可以實現返回值為指向該類本身的shared_ptr
正確的寫法應該是這樣的:
class MyPoint : public boost::enable_shared_from_this<MyPoint> { public: MyPoint(){std::cout << "MyPoint" << std::endl;} ~MyPoint(){std::cout << "~MyPoint" << std::endl;} //返回this的函數 boost::shared_ptr<MyPoint> GetPoint() { return shared_from_this(); } }; int _tmain(int argc, _TCHAR* argv[]) { boost::shared_ptr<MyPoint> p1(new MyPoint); boost::shared_ptr<MyPoint> p2 = p1->GetPoint(); std::cout << p1.use_count() << "," << p2.use_count() << std::endl; //輸出引用計數情況 p1.reset(); //內存將被釋放 }4、定制刪除器
當你在使用windows API函數進行編程時,你最煩的或許就是怎么保證申請的內核對象是否關閉,考慮一下代碼:
int *p = (int*) HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(p) ); //業務處理 //...... HeapFree( GetProcessHeap(), 0, p );對象不能通過delete來刪除,而是一個釋放函數,shared_ptr能否勝任呢,答案是肯定的。
int *p = (int*) HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(p) ); //此處使用了lambda表達式,需要vs2010或更高版本的支持 boost::shared_ptr<int> ptr(p, [](int *p){HeapFree( GetProcessHeap(), 0, p );});5、綜合應用示例
下面實現一個線程類,在線程運行結束時,能夠自行清理自己的內存
#include <set> #include <Windows.h> #include <boost/thread.hpp> class MySelf; std::set< boost::shared_ptr<MySelf> > myList; boost::thread *ptrTh; class MySelf: public boost::enable_shared_from_this<MySelf> { public: MySelf() { printf("MySelf\n"); } void StartThread() { //啟動一個線程 ptrTh = new boost::thread(&MySelf::Run, this); } void Run() { //線程任務函數 Sleep(5000); printf("stop thread\n"); Stop(); } void Stop() { //刪除自己 myList.erase(shared_from_this()); } ~MySelf() { printf("~MySelf\n"); } }; void TestSharePtr() { boost::shared_ptr<MySelf> ptr1(new MySelf); //保存到list中 myList.insert(ptr1); ptr1->StartThread(); }總結
以上是生活随笔為你收集整理的(六)boost库之内存管理shared_ptr的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: (五)boost库之随机数random
- 下一篇: (七)boost库之单例类
