在C++11中加入了很多的新特性,unique_ptr一枝獨秀,對於動態分配的內存對象,它簡單有效。雖然它不是萬能的,但是它做的已經夠好了:利用簡單的語法便可以管理動態分配的對象。
基本語法:
unique_ptr 是一個模板類,你可以很簡單地構造一個unique_ptr的對象,如下:
1
|
std::unique_ptr<foo> p(
new foo( 42 ) );</foo> |
構造完成之後,你便可以像通過一般的指針一樣來操作對象。比如operator*以及operator->操作符還是一樣如你所預期的那樣工作。
正如你可以像使用一般地指針那樣使用unique_ptr類,最最重要的還是你可以unique_ptr會在超出作用域時自動的銷燬該對象。你不必擔心在作用域的某個出口忘記delete導致內存泄露,甚至在出現異常之後它也可以自動的銷燬對象。
unique_ptr與容器:
到目前爲止,一切都是幸運的,根據標準C++的語法你同樣可以實現上面的那些種種功能,實際上,auto_ptr這個不幸者(C++11已經將其廢棄掉了)便是可以實現上述功能的,作爲一個RAII的包裹器。
不行的是,auto_ptr並不能適當的工作,即使對於一些基本的操作,auto_ptr的表現也不盡人意。例如,如果你需要創建一個存放auto_ptr的容器,那麼這將是一個大問題.
補充:關於對象與容器的關係,下面我自己寫了一段簡單的代碼以作理解:
1
2
3
4
5
6
7
8
9
10
11
12
|
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 ; } |
上述代碼的執行結果是:
unique這個詞到底意味着什麼呢?就如其字面意思一樣,當你創建一個unique_ptr時,你就宣稱這個指針就是獨一份的,沒有歧義的,就只有你可以擁有它,別人不可能也不會不經意的複製它。
foo *p = new foo(useful object);
make_use(p) ; // make_use函數的參數是一個對象指針
上面的問題我們一個也無法回答, 是因爲C++本身並沒有對怎麼使用指針這件事情作任何的約定,你只有通過查看自己的代碼,查看自己的把內存以及文檔來解決。
所幸的是,有了unique_ptr,這些問題都不是問題了,如果你傳了一個指針給另外一個例程(權當函數理解了)。你不會對該指針做一份copy(因爲它是unique的),即使你那樣做,編譯器也是不答應的。
首先來一個簡單的例子:創建一個unique_ptr,將其存放在一個容器中。作爲一個unique_ptr的新手,你可能寫出下面的代碼:
1
2
|
std::unique_ptr<foo> q(
new foo( 42 ) ); v.push_back( q );</foo> |
這似乎是合理的,但是這樣做會讓我進入一個灰色地帶:誰是這個指針的擁有者,這個容器會在它生命週期的某個時刻釋放該指針嗎?或者是還得由創建者來自己釋放它?
面對這些糾結,unique_ptr 禁止這樣的代碼。編譯這樣的代碼將會導致編譯錯誤。
Anyway,這裏的問題就是我們只允許有該指針的一份拷貝。如果你想要將該對象交給另一個對象,就必須調用move函數,也就是說你必須放棄掉該對象的擁有權。
1
|
v.push_back( std::move(q) ); |
執行完上述語句之後,q已經變成空的了,因爲q已經放棄了該對象的擁有權,將擁有權交給了容器。
move語意可以用在任何你需要創建一個“右值引用”的地方。例如下面的代碼:
返回一個unique_ptr則不需要任何特殊的代碼就可以完成。
還有,創建一個臨時的對象給一個需要unique_ptr的函數也是不需要特殊處理的。如:
process( std::unique_ptr( new foo(41) ) );
當你在使用unqiue_ptr的時候。你發現你現在需要的是一個底層的指針,那麼有兩種方式:
1
2
|
do_something( q.get() );
//retain ownership do_something_else( q.release() );
//give up ownership |
get函數是不會轉交擁有權的。 因此在大多數情況下get 函數是不提倡使用的。因爲你一旦將unique_ptr包裹的真正的指針釋放給函數使用了,那麼你就很難控制該函數到底會對這個指針做些什麼操作。也就是說你必須對你的函數謹慎再謹慎,以保證該函數只是簡單的借用一下該指針而已。
而release函數則是一個比較靠譜的方式了,當你向上述一樣對指針q調用 release時,其實你就已經宣稱說:該對象已經不歸我管了,現在就是你的了。
當你的代碼寫的比較成熟的時候,這樣的話就不會再頻繁的出現了。
還有,當unique_ptr作爲引用對象傳遞給函數的時候,如下:
1
2
3
4
|
void
inc_baz( std::unique_ptr<foo> &p ) { p->baz++; }</foo> |
因爲是傳引用,所以你完全不必要擔心該指針會被複制或者模糊了擁有者之類的事情了。
關於auto_ptr的使用,其實我們只需要在代碼中多多的使用auto關鍵字來做類型推斷,那麼實際上我們在改寫自己的代碼來使用unqiue_ptr的時候,我們不需要改變更多的用戶代碼。