c++ 11智能指针所有权和类型转换
C++ 11 Smart Pointer Ownership and Casting
我有一个基本实体类和派生类,如奶牛和鸡…
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类负责管理任何两个实体之间的关系,无论它们是否具有相同的类型。为此,它保留了一个Relationship对象列表,看起来像…
class Relationship
{
weak_ptr<Entity> from;
weak_ptr<Entity> to;
etc....
}
我使用弱指针是因为牛或鸡可能会死亡,在这种情况下,它们与其他实体的关系将失效。
这就是我的问题。我将所有内容存储为指向派生类的共享指针,但实体类中的所有代码都使用指向基类的弱指针。我经常需要将弱实体指针转换为共享母牛指针,或者将共享母牛指针转换为弱实体指针。
不知何故,我的代码允许我在上面Entity类的createrrelationship(…)的参数中传递shared_ptr对象。我真的不知道为什么会编译,我想知道这样做是否有效。我是否应该手动将其转换为弱指针,然后使用static_pointer_cast进行强制转换?(我问这个问题是因为我读到过将共享指针作为参数传递很慢,我担心会发生这种情况)。
在另一个方向上,有时我知道相同类型的两个实体之间存在某些关系。为了说明我的观点,以需要从它的父母那里继承基因的母牛为例:它通过它的关系来查找亲子关系,然后访问指向它的父母的弱实体指针。为了访问它们的遗传成员变量,它需要将这些指向实体的弱指针转换为指向奶牛的共享指针。我一直在使用weak_ptr.lock()和dynamic_pointer_cast来实现这一点。
这是执行这两个(反向相关)类型转换的有效方法吗?任何一般性的评论或参考材料都是值得赞赏的,因为我正试图有效地使用这些指针。
听起来你主要有三个问题:
- 当您必须频繁转换为
shared_ptr
以使用其值时,存储weak_ptr
是否有效? - 为什么可以用
shared_ptr<T>
构造weak_ptr<T>
? - 使用
static_ptr_cast
还是dynamic_pointer_cast
比较好
问题2是最简单的;正如Vaughn提到的,weak_ptr
有一个来自shared_ptr
的构造函数。
问题1和3更加模糊。为了解决这个问题,让我们来看看为什么你听说通过shared_ptr
很慢。当您按值传递shared_ptr
时,它必须复制shared_ptr
,并且复制它涉及底层引用计数的原子增量。这种原子增量有很多优点和缺点,但简单地说,如果您不需要跟踪所有权,这是一种不必要的开销。(在大多数情况下担心这一点可能是过早的优化,但c++语言希望确保在必要时您可以担心这类事情。)
复制weak_ptr
会比复制shared_ptr
快吗?我没有运行任何基准测试,但我猜没有。实际上有两个引用计数,一个用于拥有引用(shared_ptr
副本),一个用于非拥有引用(weak_ptr
副本)。每一种方法都有相同的原子更新要求,因此不会显著提高速度。我猜想,从理论上讲,weak_ptr
的析构函数不必检查结果引用计数并删除对象,因此,如果您所做的只是复制,则减少了一个分支。但这种用法不太可能;有可能你会通过lock()
转换回shared_ptr
。
这就把我们带回了问题1的核心。从观察weak_ptr
中获得shared_ptr
的开销有多少?大约相当于复制shared_ptr
的原子引用计数,加上消费代码上需要的分支,以确保它成功,或者在它失败时处理它。因此,与其考虑效率,不如考虑所有权和对象生命周期。您会遇到lock()
将返回null 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_pointer_cast
中的dynamic_cast
可能有可测量的开销(静态和常量强制转换几乎完全是编译时操作)。
所以我们又回到了所有权和对象生命周期。如果你有一个明确的所有权,它可以给你你想要的生命周期,如果你也在写你的性能瓶颈的代码,你会更高兴与unique_ptr
和观察原始指针(这很好;"规则"没有拥有原始指针)。但是,如果它不是瓶颈,或者如果对象生命周期不那么容易保证,shared_ptr
和weak_ptr
绝对是方便的,以尽可能小的成本来保证它们的语义。
- 有关插入适配器的错误。[错误]请求从 'back_insert_iterator<vector<>>' 类型转换为非标量类型
- 处理小于cpu数据总线的数据类型.(c++转换为机器代码)
- C++中的双指针类型转换
- 逐位操作的隐式类型转换
- 模板中的类型转换
- 在 C++(和 C)中进行类型转换时明显不一致
- 字符类型转换不兼容
- 将复杂的非基元C++数据类型转换为 Erlang/Elixir 格式,以使用 NIF 导出方法
- C++:用户定义的显式类型转换函数错误
- 将类指针类型转换为键时出错
- 通过引用传递参数时C++类型转换
- 在 C++ 中将一个模板类型的对象类型转换为另一个模板类型
- C++显式类型转换(C 样式强制转换)的强制表示法和static_cast的多种解释
- C++无效的函数类型转换
- 在将派生类指针类型转换为派生类指针后,从基类指针调用派生类函数
- 如何将Windows产品类型转换为名称?
- 通过构造函数方法输出的类到类类型转换是 5500 为什么不是 5555
- 事件系统:使用类型转换或联合进行继承
- 如何在参数中定义隐式类型转换的构造函数?
- c++ 11智能指针所有权和类型转换