首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Understding shared_ptr

Understding shared_ptr
EN

Stack Overflow用户
提问于 2015-09-03 03:46:30
回答 1查看 249关注 0票数 2

我正在阅读Scott C++,现在阅读关于管理资源的部分。他解释说,shared是一个引用计数智能指针,它的作用就像一个垃圾收集器,只是它不能中断引用的周期。什么意思?参考文献的断续周期是什么?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-09-03 03:59:32

代码语言:javascript
复制
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’来声明这些
  • 对象:控制其数据并通过会话(如调用方法或发送消息)与其进行交互的事物--即行为像代理(或与您交谈的事物,如数据库)的事物。(Sidenote:我倾向于用‘class’来声明这些

对于一个结构来说,以递归的方式共享指向其他结构的指针很少有意义。

但是,假设您有一个对象:

代码语言:javascript
复制
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的文章。

这里有一个使用它的方法:

代码语言:javascript
复制
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" ) ;

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

代码语言:javascript
复制
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放在其他地方呢?

代码语言:javascript
复制
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

添加如下命令:

代码语言:javascript
复制
db->close() ; // removes all references to Records and resets their references to the database

解决方案B

拥有一个拥有一切生命周期的“象征”:

代码语言:javascript
复制
shared_ptr<LifespanToken> token = ... ;

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

这个想法是象征性的拥有生命周期。这是避免保留周期问题的手动方法,但这需要您知道人们将如何使用该系统!你得先知道很多。这在数据库的上下文中是有意义的,但是您不希望对您的最后一段代码都这样做。值得庆幸的是,循环主要是在能够做到这一点的上下文中出现的,其中大部分可以用weak_ptr修复。

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32366622

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档