在C++11中加入了很多的新特性,unique_ptr一枝獨秀,對於動態分配的內存對象,它簡單有效。雖然它不是萬能的,但是它做的已經夠好了:利用簡單的語法便可以管理動態分配的對象。
基本語法:
unique_ptr
std::unique_ptrp( new foo(42) );
正如你可以像使用一般地指針那樣使用unique_ptr類,最最重要的還是你可以unique_ptr會在超出作用域時自動的銷毀該對象。你不必擔心在作用域的某個出口忘記delete導致內存洩露,甚至在出現異常之後它也可以自動的銷毀對象。
unique_ptr與容器:
到目前為止,一切都是幸運的,根據標准C++的語法你同樣可以實現上面的那些種種功能,實際上,auto_ptr這個不幸者(C++11已經將其廢棄掉了)便是可以實現上述功能的,作為一個RAII的包裹器。
不行的是,auto_ptr並不能適當的工作,即使對於一些基本的操作,auto_ptr的表現也不盡人意。例如,如果你需要創建一個存放auto_ptr的容器,那麼這將是一個大問題.
補充:關於對象與容器的關系,下面我自己寫了一段簡單的代碼以作理解:
class A { public: A() {cout << A ctor called... << endl;} A(const A&) {cout < vec; for(int i=0;i<5;++i) { cout << i = << i << endl; vec.push_back(A()); //構造該對象並且復制該對象。見下面代碼的運行結果 } return 0; }
可以看到,上述代碼的執行結果中調用了拷貝構造函數。
[continue] 步入正題:
C++11加入了右值引用(rvalue reference) 和 move語意(move semantic) 來解決這些問題。幸運的是,經過修復,unique_ptr可以存儲在容器中,即使容器被resize並且或者是被move都有正確的語意。並且當容器被銷毀時,這些指針管理的資源也可以被正常的銷毀。
唯一性與move語意:
unique這個詞到底意味著什麼呢?就如其字面意思一樣,當你創建一個unique_ptr時,你就宣稱這個指針就是獨一份的,沒有歧義的,就只有你可以擁有它,別人不可能也不會不經意的復制它。
比如,對於一個一般的指針,有如下代碼:
foo *p = new foo(useful object);
make_use(p) ; // make_use函數的參數是一個對象指針
這裡,我分配了一個對象並且有一個指針p指向它,當我在調用make_use函數的時,指針p會發生什麼呢?make_use會為該指針做一份拷貝嗎?在調用完畢之後會釋放掉內存嗎?或者說它就只是簡單的借用一會兒該指針就原封不動的還回來,讓調用者去釋放空間呢?
上面的問題我們一個也無法回答, 是因為C++本身並沒有對怎麼使用指針這件事情作任何的約定,你只有通過查看自己的代碼,查看自己的把內存以及文檔來解決。
所幸的是,有了unique_ptr,這些問題都不是問題了,如果你傳了一個指針給另外一個例程(權當函數理解了)。你不會對該指針做一份copy(因為它是unique的),即使你那樣做,編譯器也是不答應的。
指針的擁有者:
首先來一個簡單的例子:創建一個unique_ptr,將其存放在一個容器中。作為一個unique_ptr的新手,你可能寫出下面的代碼:
std::unique_ptrq( new foo(42) ); v.push_back( q );
面對這些糾結,unique_ptr 禁止這樣的代碼。編譯這樣的代碼將會導致編譯錯誤。
Anyway,這裡的問題就是我們只允許有該指針的一份拷貝。如果你想要將該對象交給另一個對象,就必須調用move函數,也就是說你必須放棄掉該對象的擁有權。
如:
v.push_back( std::move(q) );
執行完上述語句之後,q已經變成空的了,因為q已經放棄了該對象的擁有權,將擁有權交給了容器。
move語意可以用在任何你需要創建一個“右值引用”的地方。例如下面的代碼:
return q;
返回一個unique_ptr則不需要任何特殊的代碼就可以完成。
還有,創建一個臨時的對象給一個需要unique_ptr的函數也是不需要特殊處理的。如:
process( std::unique_ptr
Legacy Code: 老程序,其實也就是兼容性啦。
當你在使用unqiue_ptr的時候。你發現你現在需要的是一個底層的指針,那麼有兩種方式:
do_something( q.get() ); //retain ownership do_something_else( q.release() ); //give up ownership
而release函數則是一個比較靠譜的方式了,當你向上述一樣對指針q調用 release時,其實你就已經宣稱說:該對象已經不歸我管了,現在就是你的了。
當你的代碼寫的比較成熟的時候,這樣的話就不會再頻繁的出現了。
還有,當unique_ptr作為引用對象傳遞給函數的時候,如下:
void inc_baz( std::unique_ptr&p ) { p->baz++; }
關於auto_ptr的使用,其實我們只需要在代碼中多多的使用auto關鍵字來做類型推斷,那麼實際上我們在改寫自己的代碼來使用unqiue_ptr的時候,我們不需要改變更多的用戶代碼。