我正在阅读Scott Meyerses C ++,现在正在阅读有关管理资源的部分。他解释说,shared-ptr是一个引用计数智能指针,除了它不能 打破引用循环 之外,它就像垃圾收集器一样。这是什么意思?什么是引用的破坏周期?
答案 0 :(得分: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
这是一个循环。
垃圾收集器(了解系统)可以看到这些变量没有被任何东西引用,所以很明显它们不需要,并且会破坏它们。垃圾收集器[通常]可以随时运行。
但是,在C ++中,某些代码实际上必须执行该操作......但是没有任何代码可以执行。
大型课程。
首先,我想提出几个定义:
结构:包含数据的东西(如时间,位置,速度等的位置或记录) - 即你想要使用的东西最小的聪明才智。 (旁注:我倾向于使用&#39; struct&#39;声明这些内容)
对象:控制其数据并具有通过对话(如调用方法或发送消息)与之交互的状态的事物 - 即像代理一样的事物(或者,你说话的东西,比如数据库)。 (旁注:我倾向于使用&#39; class&#39;来声明这些内容)
结构以递归方式共享指向其他结构的指针很少有意义。
但是,让我们说你有一个对象:
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
有一个&#34;令牌&#34;拥有一切的生命周期:
shared_ptr<LifespanToken> token = ... ;
Database* ptr = new Database ( token ) ;
Record * record = ptr->recordNamed ( "christopher" ) ;
这个想法是令牌拥有生命周期。这是解决保留周期问题的手动方法,但这需要您了解人们将如何使用该系统!你必须事先了解很多。这在数据库的上下文中是有意义的,但是你不希望必须为你的每一段代码执行此操作。值得庆幸的是,循环主要出现在可以执行此操作的上下文中,并且其中大部分可以使用weak_ptr
进行修复。