[英]Understding shared_ptr
我正在閱讀Scott Meyerses C ++,現在正在閱讀有關管理資源的部分。 他解釋說,shared-ptr是一個引用計數智能指針,它就像一個垃圾收集器,除了它不能破壞引用循環 。 這是什么意思? 什么是引用的破壞周期?
struct A
{
shared_ptr<A> p ;
} ;
if ( true ) // complete extra scope for this example, just to make these things go out of scope
{
shared_ptr<A> p1 = make_shared<A>() ;
shared_ptr<A> p2 = make_shared<A>() ;
p1->p = p2 ;
p2->p = p1 ;
}
// At this point, they're out of scope and clearly won't be used again
// However, they will NOT be destroyed because they both have a strong reference to each other
這是一個循環。
垃圾收集器(具有系統知識)可以看到這些變量沒有被任何東西引用,所以顯然它們不是必需的,並且會破壞它們。 垃圾收集器[通常]可以隨時運行。
但是,在C ++中,一些代碼實際上必須執行該操作......但是沒有任何代碼可以執行。
大型課程。
首先,我想提出幾個定義:
結構:保存數據的東西(如時間,位置,速度等的位置或記錄) - 即您想要使用的東西,並且具有最小的智能。 (旁注:我傾向於使用'struct'聲明這些)
對象:控制其數據並具有通過對話(如調用方法或發送消息)與之交互的狀態的東西 - 即一個像代理一樣的東西(或者,與您交談的東西,如數據庫) 。 (旁注:我傾向於使用'class'聲明這些)
結構以遞歸方式共享指向其他結構的指針很少有意義。
但是,假設您有一個對象:
class Database
{
public:
// ...
void shared_ptr<Record> recordNamed ( string const& name ) const ;
private:
map<string,shared_ptr<Record> > mRecords ;
// ^ so irritating
} ;
class Record
{
public:
shared_ptr<Database> parentDatabase () const ;
void reloadFromDataStore () const ; // reloads props from database
private:
shared_ptr<Database> mpParentDatabase ;
// maybe other data here too...
} ;
shared_ptr<Database> db = ... ; // get it from somewhere
shared_ptr<Record> record = db->recordNamed ( "christopher" ) ;
這是一個真實世界的案例,你想要有循環引用,它絕對是一個有效的引用。
在垃圾收集環境中,這非常好,因為GC知道誰在使用什么,並且知道這些以后沒有被使用。
在C ++中,引用計數永遠不會變為零(因為兩個活動對象都指向彼此)並且沒有代碼可以通過它來反過來(如果有的話,它將被稱為垃圾收集器!) 。
你會在shared_ptr
之后閱讀關於weak_ptr
,它解決了這個問題。
這是使用它的一種方法:
class Database
{
public:
// ...
void shared_ptr<Record> recordNamed ( string const& name ) const ;
private:
map<string,shared_ptr<Record> > mRecords ;
} ;
class Record
{
public:
shared_ptr<Database> parentDatabase () const ;
void reloadFromDataStore () const ; // reloads props from database
private:
weak_ptr<Database> mpParentDatabase ; // don't hold onto it strongly
// maybe other data here too...
} ;
shared_ptr<Database> db = ... ; // get it from somewhere
shared_ptr<Record> record = db->recordNamed ( "christopher" ) ;
但是,如果你這樣做會發生什么?
db.reset() ; // removes the last strong-reference to the database, so it is destroyed!
db = record->parentDatabase() ; // tries to lock the weak_ptr, but fails because it's dead! So it can only return null, or throw an exception.
如果你把weak_ref
放在其他地方怎么辦?
class Database
{
public:
// ...
void shared_ptr<Record> recordNamed ( string const& name ) const ;
private:
map<string,weak_ptr<Record> > mRecords ;
} ;
class Record
{
public:
shared_ptr<Database> parentDatabase () const ;
void reloadFromDataStore () const ; // reloads props from database
private:
shared_ptr<Database> mpParentDatabase ;
// maybe other data here too...
} ;
shared_ptr<Database> db = ... ; // get it from somewhere
shared_ptr<Record> record = db->recordNamed ( "christopher" ) ;
在這種情況下, Database
只對其記錄有弱引用......但這意味着每次需要某些當前不存在的東西時,它必須從商店創建它。 這適用於基於文件的數據庫,但如果數據庫純粹作為程序的一部分存在於內存中,則不會。
添加如下命令:
db->close() ; // removes all references to Records and resets their references to the database
擁有擁有一切生命周期的“代幣”:
shared_ptr<LifespanToken> token = ... ;
Database* ptr = new Database ( token ) ;
Record * record = ptr->recordNamed ( "christopher" ) ;
這個想法是令牌擁有生命周期。 這是解決保留周期問題的手動方法,但這需要您了解人們將如何使用該系統! 你必須事先了解很多。 這在數據庫的上下文中是有意義的,但您不希望必須為代碼的每一段都執行此操作。 值得慶幸的是,循環主要出現在可以執行此操作的上下文中,並且大多數循環可以使用weak_ptr
進行修復。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.