3

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

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.

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

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

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

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

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

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

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

4

1 回答 1

3

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

  1. weak_ptr当您必须经常转换shared_ptr以使用其价值时,存储是否有效?
  2. 为什么可以weak_ptr<T>从 a 构造 a shared_ptr<T>
  3. 你最好使用static_ptr_cast还是dynamic_pointer_cast

问题 2 是最简单的;正如Vaughn提到的,weak_ptr有一个来自.shared_ptr

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

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

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

最后回到问题 3。我在没有任何真正了解这些类型的情况下在这里回答;相反,我是基于 a 的shared_ptr工作原理。static_pointer_castdynamic_pointer_castconst_pointer_castreturn中的每一个都shared_ptr指向同一个底层对象。因此,他们执行了其引用计数的原子增量。所以它们的开销大致是静态或动态或 const 转换加上shared_ptr复制构造函数的开销。该shared_ptr部分不太可能对您的整个程序很重要,并且在转换部分中,只有dynamic_castadynamic_pointer_cast可能有可衡量的费用(静态和 const 转换几乎完全是编译时操作)。

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

于 2014-05-19T02:18:50.963 回答