簡體   English   中英

下載shared_ptr

[英]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只對其記錄有弱引用......但這意味着每次需要某些當前不存在的東西時,它必須從商店創建它。 這適用於基於文件的數據庫,但如果數據庫純粹作為程序的一部分存在於內存中,則不會。

解決方案A.

添加如下命令:

db->close() ; // removes all references to Records and resets their references to the database

解決方案B.

擁有擁有一切生命周期的“代幣”:

shared_ptr<LifespanToken> token = ... ;

Database* ptr = new Database ( token ) ;
Record * record = ptr->recordNamed ( "christopher" ) ;

這個想法是令牌擁有生命周期。 這是解決保留周期問題的手動方法,但這需要您了解人們將如何使用該系統! 你必須事先了解很多。 這在數據庫的上下文中是有意義的,但您希望必須為代碼的每一段都執行此操作。 值得慶幸的是,循環主要出現在可以執行此操作的上下文中,並且大多數循環可以使用weak_ptr進行修復。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM