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

时间:2014-05-19 01:34:43

标签: 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...

我正在努力学习使用std智能指针正确管理内存。我现在正在做的事情,我的派生类生活的唯一地方是共享指针的向量,如...

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

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

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

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

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

不知何故,我的代码允许我在上面的Entity类中的createRelationship(...)中的参数中传递shared_ptr对象。我不知道为什么编译,我想知道这样做是否有效。我应该手动将其转换为弱指针然后使用static_pointer_cast进行转换吗? (我问这个是因为我已经读到传递共享指针作为参数很慢,我担心它会继续)。

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

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

1 个答案:

答案 0 :(得分:3)

听起来你有三个主要问题:

  1. 当您必须经常转换为weak_ptr以使用其值时,是否有效存储shared_ptr
  2. 为什么可以从weak_ptr<T>构建shared_ptr<T>
  3. 您最好使用static_ptr_castdynamic_pointer_cast
  4. 问题2是最简单的;正如Vaughn所提到的,weak_ptr来自shared_ptr的{​​{3}}。

    问题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_castdynamic_pointer_castconst_pointer_cast返回shared_ptr个实例,指向同一个底层对象。因此,他们已经执行了其引用计数的原子增量。因此,它们的开销大致是静态或动态或const转换加上shared_ptr复制构造函数的开销。 shared_ptr部分不太可能对您的整体计划和铸造部分有重要影响,只有dynamic_cast的{​​{1}}可能会产生可衡量的费用(静态和const转换几乎完全是编译时操作)。

    因此,我们又一次回到了所有权和对象的生命周期。如果您拥有明确的所有权并且能够为您提供所需的生命周期,并且如果您还要编写处于性能瓶颈的代码,那么您将更加满意dynamic_pointer_cast并观察原始指针(以及#39;很好;&#34;规则&#34;不是拥有原始指针)。但是,如果它不是瓶颈,或者对象的生命周期不容易保证,unique_ptrshared_ptr绝对是方便,只需要尽可能小的成本来保证它们的语义。< / p>