C++ 11 智能指针所有权和转换

标签 c++ shared-ptr software-design weak-ptr

我有一个基础实体类和派生类,比如牛和鸡......

using namespace std;
class Entity
{
    list<shared_ptr<Relationship>> relationships;
    void createRelationship(weak_ptr<Entity> other,.... other stuff)
    //...
    virtual ~Entity()
}
class Cow: public Entity
{
    //...
}
class Chicken : public Entity
....etc...

我正在尝试学习使用标准智能指针正确管理内存。我现在做事的方式是,我的派生类唯一存在的地方是共享指针的 vector ,例如......

vector<shared_ptr<Cow>>
vector<shared_ptr<Chicken>>
etc.

我的实体类负责管理任意两个实体之间的关系,无论它们是否具有相同的类型。为此,它保留了一个看起来像...的关系对象列表。

class Relationship
{
  weak_ptr<Entity> from;
  weak_ptr<Entity> to;
etc....
}

我使用弱指针是因为牛或鸡可能会死亡,在这种情况下它们与其他实体的关系应该失效。

所以这是我的问题。我将所有内容存储为指向派生类的共享指针,但我的实体类中的所有代码都使用指向基类的弱指针。我会经常需要将弱实体指针转换为共享 Cow 指针或将共享 Cow 指针转换为弱实体指针。

不知何故,我的代码允许我在上面实体类的 createRelationship(...) 的参数中传递 shared_ptr 对象。我真的不知道为什么要编译,我想知道这样做是否有效。我是否应该手动将它变成一个弱指针,然后使用 static_pointer_cast 进行转换? (我问这个是因为我读到过将共享指针作为参数传递很慢,我担心这种情况会发生)。

另一方面,有时我知道某些关系存在于同一类型的两个实体之间。为了说明我的观点,一头需要从其 parent 那里继承其遗传基因的小母牛:它搜索其关系以找到亲子关系,然后获得指向其 parent 的弱实体指针。为了访问它们的遗传成员变量,它需要将这些指向实体的弱指针转换为指向奶牛的共享指针。我一直在使用 weak_ptr.lock() 然后是 dynamic_pointer_cast 来完成这个。

这是执行这两个(反向相关)转换的有效方式吗?任何一般性评论或引用资料都值得赞赏,因为我正试图有效地使用这些指针。

最佳答案

听起来您主要担心三个问题:

  1. 正在存储weak_ptr当您必须频繁转换为 shared_ptr 时效率很高使用它的值(value)?
  2. 为什么可以构造一个weak_ptr<T>来自shared_ptr<T>
  3. 你最好还是使用 static_ptr_castdynamic_pointer_cast

第2题最简单;正如沃恩提到的,weak_ptr有一个constructor来自 shared_ptr .

问题 1 和 3 更加模糊。为了解决这个问题,让我们来看看为什么你听说过 shared_ptr是缓慢的。当你路过一个 shared_ptr按值,它必须复制 shared_ptr ,并复制它涉及底层引用计数的原子增量。这种原子增量有很多优点和缺点,但简短的版本是,如果您不需要跟踪所有权,这是不必要的开销。 (在大多数情况下,担心这一点很可能是过早的优化,但 C++ 语言希望确保您有可能在事实证明有必要时担心这类事情。)

复制一个weak_ptr怎么样? -- 这比复制 shared_ptr 快吗? ?我没有运行任何基准测试,但我猜不会。实际上有两个引用计数,一个用于拥有引用(shared_ptr 份),一个用于非拥有(weak_ptr 份)。这些中的每一个都将具有相同的原子更新要求,因此不会明显更快。我猜理论上 weak_ptr 的析构函数不必检查生成的引用计数并删除对象,因此如果您所做的只是复制,那将减少一个分支。但这是不太可能的用法;您可能会转换回 shared_ptr通过lock() .

这让我们回到了问题 1 的核心。获得 shared_ptr 需要多少开销?来自观察weak_ptr ?大约与复制 shared_ptr 的原子引用计数一样多,加上您在使用代码上需要的分支,以确保它成功,或在失败时处理。因此与其在这里考虑效率,不如考虑所有权和对象生命周期。您会遇到 lock() 的情况吗?将返回空 shared_ptr ?如果没有,您很可能可以通过观察原始指针来逃脱。如果标的shared_ptr对象可能会在 weak_ptr 之前消失确实如此,您需要对其进行过期检查。如果这是一个瓶颈,看看你是否能找到保证生命周期的方法。

最后回到问题 3。我在这里回答时并没有真正了解这些类型;相反,我是基于如何 shared_ptr作品。每个 static_pointer_cast , dynamic_pointer_cast , 和 const_pointer_cast返回 shared_ptr指向相同底层对象的实例。因此,他们执行了其引用计数的原子增量。因此,它们的开销大致是静态或动态或 const 转换的开销加上 shared_ptr 的开销。复制构造函数。 shared_ptr部分不太可能对您的整个程序很重要,而在类型转换部分中,只有 dynamic_castdynamic_pointer_cast可能会有可衡量的费用(静态和常量转换几乎完全是编译时操作)。

所以我们又一次回到了所有权和对象生命周期。如果您拥有明确的所有权并且可以为您提供所需的生命周期,并且您还在编写性能瓶颈的代码,那么您会更高兴 unique_ptr并观察原始指针(这很好;“规则”是不拥有原始指针)。但如果它不是瓶颈,或者如果对象生命周期不是那么容易保证,shared_ptrweak_ptr绝对是用尽可能小的成本来保证它们的语义的便利。

关于C++ 11 智能指针所有权和转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23728587/

相关文章:

c++ - char 是唯一具有强制大小的类型吗?

c++ - boost::array 不在 VS 2005 上编译

c++ - 试图获取从文件中读取的随机素数之和?

php - 如何根据数据库值动态定义常量?

architecture - 敏捷解决方案设计

go - 为什么我们在 Go 调度器的设计中添加 P 而不是简单地更改 M?

c++ - Python:获取两个大写字母之间的字符串

c++ - std::shared_ptr 指向的对象何时被删除?

c++ - shared_ptr 和私有(private)继承

c++ - 给自己一个 std::shared_ptr<std::thread> 。定义或未定义的行为