下载shared_ptr

ste*_*lla 2 c++ memory-management

我正在阅读Scott Meyerses C++,现在正在阅读有关管理资源的部分.他解释说,shared-ptr是一个引用计数智能指针,它就像一个垃圾收集器,除了它不能破坏引用循环.这是什么意思?什么是引用的破坏周期?

iAd*_*nct 7

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
Run Code Online (Sandbox Code Playgroud)

这是一个循环.

垃圾收集器(具有系统知识)可以看到这些变量没有被任何东西引用,所以显然它们不是必需的,并且会破坏它们.垃圾收集器[通常]可以随时运行.

但是,在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" ) ;
Run Code Online (Sandbox Code Playgroud)

这是一个真实世界的案例,你想要有循环引用,它绝对是一个有效的引用.

在垃圾收集环境中,这非常好,因为GC知道谁在使用什么,并且知道这些以后没有被使用.

在C++中,引用计数永远不会变为零(因为两个活动对象都指向彼此)并且没有代码可以通过它来反过来(如果有的话,它将被称为垃圾收集器!).

你会读到后shared_ptrweak_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" ) ;
Run Code Online (Sandbox Code Playgroud)

但是,如果你这样做会发生什么?

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.
Run Code Online (Sandbox Code Playgroud)

如果你把它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" ) ;
Run Code Online (Sandbox Code Playgroud)

在这种情况下,Database唯一对其记录的弱引用...但这意味着它必须每次从商店创建它时,它需要一些当前不存在的东西.这适用于基于文件的数据库,但如果数据库纯粹作为程序的一部分存在于内存中,则不会.

解决方案A.

添加如下命令:

db->close() ; // removes all references to Records and resets their references to the database
Run Code Online (Sandbox Code Playgroud)

解决方案B.

拥有拥有一切生命周期的"代币":

shared_ptr<LifespanToken> token = ... ;

Database* ptr = new Database ( token ) ;
Record * record = ptr->recordNamed ( "christopher" ) ;
Run Code Online (Sandbox Code Playgroud)

这个想法是令牌拥有生命周期.这是解决保留周期问题的手动方法,但这需要您了解人们将如何使用该系统!你必须事先了解很多.这在数据库的上下文中是有意义的,但您希望必须为代码的每一段都执行此操作.值得庆幸的是,周期主要出现在可以做到这一点的环境中,并且大多数都可以修复weak_ptr.