C++11 Smart Pointers2015-03-10 Matthew
Outline
● Why smart pointer?● std::unique_ptr● std::shared_ptr● std::weak_ptr● Conclusion
Why smart pointer?
當我們拿到一個 Foo* ptr 時...
1. 你其實不知道它是單一物件還是 array.2. 你不知道該怎麼銷毀它.
a. delete ? delete[] ? ->Release()? Finalize()?
3. 保證(考慮所有路線可能後)剛好 delete 一次.4. 你不知道這個指標是否還有效...
…from Effective Modern C++
Smart pointer!
● C++11 引入三種 smart pointer 來管理資源.● #include <memory>
● unique_ptr: for exclusive-ownership.● share_ptr: for shared-ownership.● weak_ptr: if pointers that can dangle.
for exclusive-ownership
std::unique_ptr
std::uniqre_ptr
● 最簡單的 smart pointer● 適用於物件只有一個 owner 時● 當 unique_ptr 消滅時, 會順帶釋放持有資源.
適用場景
void f()
{
ClassA* ptr = new ClassA; … // do something
delete ptr;
}
if ( error ) { return; }
適用場景
void f()
{
std::unique_ptr<ClassA> ptr(new ClassA);
… // do something
if ( error ) { return; }
//delete ptr; no longer necessary.
}
建立 unique_ptr
// 傳入 raw pointer
unique_ptr< string > up( new string("nico") );
// 使用 make_unique function
auto up = make_unique< string >( "nico" );
std::unique_ptr 用法
// unique_ptr 覆寫了 *, -> 運算子
// 所以用法和一般 pointer 差不多
(*up)[0] = 'N';
up->append("lai");
cout << *up << endl;
std::unique_ptr 管理動態分配陣列
// 也可以管理陣列, 用[]運算子存取
unique_ptr< int[] > up2( new int[10] );
for ( int i = 0; i < 10; ++i)
cout << up2[ i ];
std::unique_ptr 檢查
// 檢查指標有效性
if ( up ) { … }
if ( up != nullptr ) { … }
// 釋放所有權 (立刻釋放資源)
up.release();
std::unique_ptr 用法
// 不接受隱式轉換成 raw pointer (Compiler Error!)
unique_ptr<string> up = new string("");
string* p = up;
// 如果你真的要取得 raw pointer…string* p = up.get(); //後果自行負責
// 不能分享所有權, 也不能copy unique_ptr
unique_ptr<string> up2 = up; (Compiler Error!)
// 要用 std::move 顯式轉移
unique_ptr<string> up2 = std::move(up);
// 轉移之後up失去所有權, onwer 變成了up2
cout << (up == nullptr); // true
cout << (up2 == nullptr); // false
std::unique_ptr tips
vector< unique_ptr<string> > kVec;
// up 不能複製, 要用 reference 的方式取
for ( auto& up : kVec )
{
cout << *up;
}
std::unique_ptr 很適合持有 memberclass MyClass
{
unique_ptr<Foo> m_spFoo;
public:
MyClass() : m_spFoo( make_unique<Foo>( 1, "Bar" ) ) {}
~MyClass() { /* 不需要在 destructor 釋放資源 */ }
void DoSomething()
{
m_spFoo->bar();
}
};
for shared-ownership.
std::shared_ptr
std::shared_ptr 概念
● 由多個物件共享該資源的持有權
● 允許多個 smart pointer 指向同一個物件
● 只有所有的 shared_ptr 都消滅後,才會釋放
持有資源 (reference count)
建立 std::shared_ptr
// 傳入 raw_pointer
shared_ptr<string> sp( new string("nico") );
// 呼叫 make_shared function (Recommend!)
// make_shared is more efficient.
auto sp = make_shared<string>( "nico" );
std::shared_ptr 用法
auto sp = make_shared<string>( "nico" );
cout << (*sp)[ 0 ]; // print 'n'
sp->replace(0, 1, 'N'); // "Nico"
string* p = sp.get(); // get raw pointer
if ( sp ) { … }
if ( sp != nullptr ) { … }
if ( sp == nullptr ) { … }
std::shared_ptr 可以分享持有權
auto sp = make_shared< QImage >( "icon.png" );
auto sp2( sp ); // via copy constructor
auto sp3 = sp; // via assignment
DoSomething( sp ); // pass as parameter
sp.reset(); // 釋放所有權
sp2.reset( new QImage ); // 改持有另一個 raw ptr
std::shared_ptr 可以分享持有權
auto sp = make_shared< string >( "nico" );
auto sp2 = sp;
vector< shared_ptr<string> > kVec;
kVec.push_back( sp );
kVec.push_back( sp2 );
cout << sp.use_count() << endl; // 4
specify deleter// shared_ptr 可以指定釋放資源的方式
FILE* fp = fopen("log.txt", "w");
shared_ptr<FILE> sp( fp, CloseFile );
void CloseFile( FILE* f )
{
cout << "File Closed!";
fclose( f );
}
specify deleter// shared_ptr 可以指定釋放資源的方式
FILE* fp = fopen("log.txt", "w");
shared_ptr<FILE> sp( fp, [] ( FILE* f )
{
cout << "File Closed!";
fclose(f);
} );
type cast
// static_pointer_cast()
// dynamic_pointer_cast()
// const_pointer_cast()
void f( share_ptr<CBase> spBase )
{
share_ptr<CNode> spNode =
dynamic_pointer_cast< CNode >( spBase );
}
if pointers that can dangle.
std::weak_ptr
std::weak_ptr 概念
● 有時候我們不希望共享物件的"持有權"…● 但是又不想用 raw pointer…
● 用 weak_ptr!● weak_ptr 可以存取 shared_ptr 管理的物件,
但是不會增加 ref count.
避免 reference cycle
Bank
Customershare_ptr<Customer>
share_ptr<Bank>
無法自動釋放!!share_ptr<Bank>
避免 reference cycle
Bank
Customershare_ptr<Customer>
weak_ptr<Bank>
自動釋放OK!!share_ptr<Bank>
建立 weak_ptr
// weak_ptr 不能單獨存在!
weak_ptr<QImage> wp( new QImage );
// 必須依附於 shared_ptr.
shared_ptr<QImage> sp( new QImage("icon.png") );
weak_ptr<QImage> wp( sp );
weak_ptr 用法
// weak_ptr 「沒有」覆寫 *, -> 運算子
// 所以不能像一般 pointer 使用
weak_ptr< string > wp( sp );
(*wp)[0] = 'N'; // Compiler Error!
wp->append("lai"); // Compiler Error!
// 必須呼叫 lock() 轉成 share_ptr 才能用
weak_ptr< string > wp( sp2 );
if ( auto& sp = wp.lock() )
{
sp->append( "lai" );
cout << *sp;
}
else
{
cout << "Object is deleted.";
}
weak_ptr 用法
weak_ptr< string > wp( sp );
// weak_ptr 可以 copy
weak_ptr< string > wp2 = wp;
wp.reset(); // 釋放物件指標
wp.expired(); // 詢問物件還在否?
auto sp = make_shared<string>( "Hello" );
weak_ptr<string> wp( sp );
cout << *sp ; // print 'Hello'
cout << *wp.lock() ; // print 'Hello'
cout << wp.expired(); // print 'false'
sp.reset(); // release resource.
cout << wp.expired(); // print 'true'
cout << *wp.lock(); // throw exception!
Dangerous! Don't do that.
CImage* pImage = new CImage;
// 不要用兩個 smart pointer 管理同一個 raw pointer !!
share_ptr<CImage> sp( pImage );
share_ptr<CImage> sp2( pImage );
Conclusion
● C++11 想要消滅 delete keyword.● C++11 想要消滅 90% 的 new keyword.● 思考資源的持有權, 來挑選 smart pointer.● 如果不確定要用哪個, 先選 unique_ptr
○ 因為 unique_ptr 幾乎沒有 cost
● share_ptr 有一些 cost○ double pointer size (space cost)○ reference count. (performance cost)
Reference
● http://www.cplusplus.com/reference/memory/● MSDN: How to: Create and Use shared_ptr Instances● The C++ Standard Library 2ed. (Book)● Effective Modern C++ (Book)