shared_ptr <>是为了weak_ptr <>作为unique_ptr <>是…什么?
在C ++ 11中,您可以使用shared_ptr<>
与对象或variables以及weak_ptr<>
build立所有权关系,以非所有方式安全地引用该对象。
您还可以使用unique_ptr<>
与对象或variablesbuild立所有权关系。 但是,如果其他非拥有对象也想引用该对象呢? weak_ptr<>
在这种情况下没有帮助。 原始指针是有帮助的,但带来了各种缺点(例如,它们可以自动初始化为nullptr,但是这是通过与std::*_ptr<>
types不一致的技术来实现的)。
对于通过unique_ptr<>
拥有的对象的非拥有引用,等价于weak_ptr<>
是什么?
这是一个澄清的例子,类似于我正在做的游戏中的某些东西。
class World { public: Trebuchet* trebuchet() const { return m_trebuchet.get(); } private: std::unique_ptr< Trebuchet > m_trebuchet; }; class Victim { public: Victim( Trebuchet* theTrebuchet ) : m_trebuchet( theTrebuchet ) {} ~Victim() { delete m_trebuchet; // Duh. Oops. Dumb error. Nice if the compiler helped prevent this. } private: Trebuchet* m_trebuchet; // Non-owning. }; shared_ptr< Victim > createVictim( World& world ) { return make_shared< Victim >( world.trebuchet() ); }
在这里,我们使用一个原始的指针来维护一个非拥有的关系,这个关系是通过其他地方的unique_ptr<>
拥有的。 但是我们能做的最好的吗?
希望是一种指针:
- 看起来像其他现代指针types。 例如
std::raw_ptr<T>
。 - replace原始指针,以便使用现代指针types的代码库可以通过search
_ptr<
(大致)find所有指针。 - 自动初始化为nullptr。
从而:
int* p; // Unknown value. std::raw_ptr< int > p; // null.
这种types现在是否已经存在于C ++中,是为未来提出的,还是在Boost中广泛使用的另一种实现?
shared_ptr
的“通知”行为需要对引用计数控制块进行引用计数。 shared_ptr
的引用计数控制块为此使用单独的引用计数。 weak_ptr
实例保持对该块的引用,并且weak_ptr
本身防止引用计数控制块被delete
。 指向的对象在强计数到零时调用它的析构函数(这可能会或可能不会导致存储该对象的内存的delete
),并且仅当弱引用计数发生时才delete
控制块归零。
unique_ptr
的宗旨是它在零指针上没有任何开销。 分配和维护引用计数控制块(以支持weak_ptr
-ish语义)打破了这个原则。 如果您需要该描述的行为,那么即使对该对象的其他引用是非拥有的,您也确实需要共享语义。 在这种情况下仍然有共享 – 分享对象是否被破坏的状态。
如果您需要generics非引用引用并且不需要通知,请使用普通指针或纯引用指向unique_ptr
的项目。
编辑:
在你的例子中,看起来Victim
应该要求Trebuchet&
而不是Trebuchet*
。 那么究竟是谁拥有这个对象呢?
class World { public: Trebuchet& trebuchet() const { return *m_trebuchet.get(); } private: std::unique_ptr< Trebuchet > m_trebuchet; }; class Victim { public: Victim( Trebuchet& theTrebuchet ) : m_trebuchet( theTrebuchet ) {} ~Victim() { delete m_trebuchet; // Compiler error. :) } private: Trebuchet& m_trebuchet; // Non-owning. }; shared_ptr< Victim > createVictim( World& world ) { return make_shared< Victim >( world.trebuchet() ); }
真正需要一个标准的指针types来作为std::unique_ptr<>
的非拥有的,廉价的,行为良好的对应点。 没有这样的指针已经被标准化了,但是一个标准已经被提出并正在被C ++标准委员会讨论。 “World's Dumbest智能指针”,也就是std::exempt_ptr<>
应该具有其他现代C ++指针类的通用语义,但是对拥有指向对象(如shared_ptr
和unique_ptr
)或者对于正确响应删除那个对象(就像weak_ptr
一样)。
假设这个特点最终得到了委员会的批准,就完全可以满足这个问题所强调的需要。 上述链接文件即使未经委员会批准,也充分expression了需要,并形成了完整的解决scheme。
unique_ptr
的非unique_ptr
模拟是一个普通的C指针。 有什么不同 – C指针不知道指向的数据是否仍然可以访问。 另一方面weak_ptr
。 但是不可能用一个知道数据有效性的指针来replaceraw
指针,而无需额外的开销(而weak_ptr
确实有这样的开销)。 这意味着C-style指针在速度方面是最好的,你可以作为unique_ptr
的非unique_ptr
模拟。
虽然你无法获得一个“弱”的指针到一个独特的拥有对象是免费的,这个概念是有用的,并在一个系统中使用。 请参阅Chromium的WeakPtr和QT的QPointer实现。
Chromium的WeakPtr通过在弱引用对象内部存储shared_ptr并在对象被销毁时将其标记为无效来实现。 WeakPtrs然后引用该ControlBlock并检查它是否有效,然后交出其原始指针。 我认为QT的QPointer是相似的。 由于所有权不共享,原始对象被确定性地销毁。
但是 ,这意味着取消引用WeakUniquePtr
不是线程安全的:
主题1:
unique_ptr<MyObject> obj(new MyObject); thread2.send(obj->AsWeakPtr()); ... obj.reset(); // A
线程2:
void receive(WeakUniquePtr<MyObject> weak_obj) { if (MyObject* obj = weak_obj.get()) { // B obj->use(); } }
如果A
行恰好与B
行同时运行,那么线程2将使用一个悬挂指针结束。 std::weak_ptr
可以通过在让线程2使用它之前自动获取对该对象的共享拥有引用来防止这个问题,但这违反了上面假设该对象是唯一拥有的假设。 这意味着任何对WeakUniquePtr
使用WeakUniquePtr
需要与对象的销毁同步,最简单的方法是要求它们在同一个线程的消息循环中完成。 (请注意,在使用它之前,将WeakUniquePtr
在线程之间来回复制仍然是完全安全的。)
人们可以想象在std::unique_ptr
使用自定义删除器来实现这个使用标准库types,但是这留给读者的练习。
boost::optional<Trebuchet&>
正如比利·奥尼尔(Billy ONeal)在他的回答中指出的,你可能想要通过一个Trebuchet&
而不是一个指针。 引用的问题是你不能传递一个nullptr
, boost::optional
提供了一个方法来获得一个nullptr
的equivilent。 有关boost :: optional的更多详细信息,请访问: http : //www.boost.org/doc/libs/1_54_0/libs/optional/doc/html/boost_optional/detailed_semantics.html
另请参阅此问题: boost :: optional <T&> vs T *
注意: std::optional<T>
正在使其成为C ++ 14,但是std::optional<T&>
是一个单独的提议,它不在当前的C ++ 14草案中。 更多详细信息,请访问: http : //www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3672.html
在带有shared_ptr,weak_ptr和unique_ptr的新C ++环境中,不应该使用原始指针或引用来存储对象的长期引用,比如trebuchet。 相反,世界应该有一个shared_ptr的trebuchet和受害者应该存储一个shared_ptr或weak_ptr,根据是否投石机如果世界消失应该坚持与受害者。 使用weak_ptr可以让你知道指针是否仍然有效(即世界仍然存在),没有办法用原始指针或引用来做到这一点。
当你使用unique_ptr时,你声明只有World实例才拥有投石机。 World类的客户端可以通过调用“get”方法来使用World对象的trebuchet,但是当它们完成使用时,不应该保持方法返回的引用或指针。 相反,他们应该通过调用“get”方法来每次他们想要使用它时“借用”trebuchet。
上面说的可能是你想要存储一个引用或原始指针以备将来使用的情况,以避免shared_ptr的开销。 但是这些实例很less,而且你需要完全确定在拥有投石机的World对象消失之后你不会使用指针或引用。
一个带有原始指针或引用的函数隐含地保证函数返回后不会保留该指针的副本。 作为回报,调用者承诺指针有效(或nullptr
),直到被调用者返回。
如果你想坚持指针,你正在分享它(并应使用shared_ptr
)。 unique_ptr
pipe理指针的单个副本。 您使用原始指针(或引用)来引用涉及该对象的调用函数。
这对于shared_ptr
对象是一样的。 weak_ptr
只有当你想要额外引用超过所涉及的函数的指向太对象时才起作用。 weak_ptr的主要用途是中断两个对象之间的引用(因此不会释放)引用循环。
不过请记住,采用shared_ptr
或weak_ptr
意味着采用该参数的函数将(可选地)修改某个其他对象,以保留对指向该对象的引用,该引用超出了该函数的调用。 在绝大多数情况下,即使对于shared_ptr或weak_ptr,您也使用原始指针(如果nullptr
是有效的值)或ref(当值保证时)。