C++11对unique_ptr的非拥有引用/指针
C++11 non-owning reference/pointer to unique_ptr?
这不是"如何做"的问题,而是"如何以正确的方式做"
我正在Qt中开发一个编辑器,其中不同的小部件显示子级及其(成员)变量。这些小部件中的每一个都应该保存一个指向编辑过的子部件的引用/指针,以显示和更改其成员变量。
第一次尝试是用旧的ANSI C方法学习(现在仍然有点坚持),使用指向所用对象的简单原始指针。它工作得很好,但由于C++11标准支持智能指针,并且建议使用它们,我正在尝试使用它们。
问题是,我不太确定在这种情况下使用它们的"最佳方式"是什么。。。看完《聪明的指针》:或者谁拥有你的孩子?我什么时候用哪种指针?和其他一些人,我得出了不同的结论:
第一种是使用*unique_ptr
,因为编辑的对象显然是创建和删除其子对象的所有者。小部件只是指代子部件来显示或更改它们。问题是小部件应该如何引用子。。。
现在,我只是简单地使用unique_ptr
的get()
方法得到的原始指针,但这对我来说似乎有点缺陷。我仍然可以意外地在指针上调用delete并取消智能指针的好处。
第二种方法是使用shared_ptr
,因为许多对象都引用了子对象并对其进行编辑。此外,在一个小部件中意外删除它不会造成伤害,因为它仍然由其他对象所有。问题是他们拥有它。当我想从编辑过的对象中删除它时,我还必须在它真正消失之前通知所有小部件删除它。(这再次看起来有缺陷且容易出错)
我真的不满足于这两种方式。有没有一种干净的方法可以指向对象的unique_ptr
子级?还是我错过了一个完全不同的、更好的方法来解决这个问题?
您的用例不会直接转化为需求(如果在您编辑小部件时其他人删除了它怎么办?)但我认为您不需要裸指针之外的任何东西。
标准库不提供任何严格的指针观察器类。观察员实体:
- 本机类型的可为Null、可变指针(
T *
) - 本机类型的不可为Null、不可变的引用(
T &
) - 类类型(
std::reference_wrapper<T>
)的不可为null、可变引用/代理 - 指向托管对象的可为Null、自验证、可变指针(
std::weak_ptr<T>
)
如果您想要一个指向非托管对象的不可为null、可变的指针,这是一个相当合理的要求,那么您可以滚动自己的指针。
但裸指针并没有那么糟糕。唯一的区别是它可以是nullptr
,并且在名称空间std
中没有一个漂亮的、长的、明确的名称。
您希望使用shared_ptr
来代替unique_ptr
,使用weak_ptr
来代替原始指针。这会给你想要的东西。weak_ptr
不会干扰编辑对象删除底层对象的能力。
如果您正在使用Qt,您可能会考虑使用Qt智能指针,而不是std::smart-points:
QShardPointer
和QWeakPointer
或对于QObjects:
QPointer
(或QWeakPointer
,特别是在Qt4中)
或实现写时复制数据共享,Qt容器和QString风格:
QSharedDataPointer
还有其他指针类,但上面的一些类最有可能实现您想要的功能。同样重要的是,如果您的数据在Qt容器类QStrings
或类似的类中,它们会使用写时复制语义处理自己的内存,并且通常应该作为纯值(有时作为引用)而不是指针传递。
但最重要的是,不要将std::unique_ptr
或std::shared_ptr
与具有父对象的QObjects一起使用,因为如果父对象首先删除子对象,那么std::指针将再次删除它,从而使程序崩溃(其他方式也可以,子对象将通知其父对象它已删除)。换句话说,如果你混合了QObjects和std::pointers,很有可能会出现微妙的错误,所以不要这样做。从你的问题中还不清楚你是否在这样做,只是以防万一。
有一个关于世界上最愚蠢的智能指针的提议,它是一个非拥有指针。基本上,它是一个T*
,上面写着"哦,顺便说一句,我不声称对这些数据有任何所有权"。
在这种情况下,如果unique_ptr
消失,您必须管理观察/被动/哑指针被重置——半手动寿命管理。在拥有上述内容之前,使用一个名称指示其为非所有者的原始T*
同样有效。
执行上述操作有好处,尤其是当您拥有具有依赖生存期的对象时。
如果不这样做,则shared_ptr
和weak_ptr
可以工作。但是,请注意,任何具有weak_ptr
的人都可以创建新的shared_ptr
,这可以将共享对象的生存期延长到原始shared_ptr
之外。这是必须发生的,因为在其他情况下存在竞争条件,即weak_ptr
的用户确保其数据有效,然后使用它,但在此之前,shared_ptr
的数据已被销毁。
因此,weak_ptr
必须能够防止shared_ptr
被破坏(在多线程上下文中阻塞,导致它在单线程上下文中以某种方式"失败")。在这种情况下,"失败"包括延长生存期,这既解决了多线程问题,也解决了单线程问题。
(单线程的问题是,你要验证weak_ptr
是否良好,做一些导致shared_ptr
重置的事情,然后继续想使用weak_ptr
的数据而不重新检查。当然,除了例外,这是可以避免的,但仔细检查后,你会遇到问题,在访问this
之前,你必须对所有方法进行编码,以检查是否删除,如果this
被删除,则抛出d、 非常苛刻的要求!)
- 将对象数组的引用传递给函数
- 什么时候在C++中返回常量引用是个好主意
- 我想将一个对T类型的非常量左值引用绑定到一个T类型的临时值
- 何时在引用或唯一指针上使用移动语义
- 如何在c++中使用引用实现类似python的行为
- 编译C++时未定义的引用
- Ctypes wstring通过引用传递
- c++r值引用应用于函数指针
- 在 c++ 中拥有一组结构的正确方法是什么?
- 理解c++中的引用
- 拥有映射的现代方法,该映射可以指向或引用已在堆栈上分配的不同类型的数据
- condition_variable、引用和线程:谁拥有锁?
- CBasePin 递增对拥有过滤器的引用.循环引用?
- 是否可以使用对shared_ptr拥有的对象的引用
- 类,该类拥有对自身的引用
- lock_guard始终拥有引用互斥锁的锁定模式是什么意思
- 存储非拥有引用的对象,必须在销毁引用之前通知该引用
- C++11对unique_ptr的非拥有引用/指针
- 如何在c++中拥有私有成员变量和对它们的引用
- range-v3 的"partial_sum"如何不与非拥有引用语义相矛盾?