[英]Is there such thing as a weak_ptr that can't be locked (promoted to shared_ptr)? If not, why?
也许以前有人问过这个问题,但我从未找到满意的答案。 另外,为简单起见,假设我在谈论单线程应用程序。
因此,我多次听到的是,如果您拥有一个非拥有的对象并且其寿命得到保证 ,则应该使用原始指针进行引用。 对象的所有者将使用unique_ptr,并根据需要分发原始指针。
但是,如果对象是非所有者的 ,并且不能保证生存期 ,该怎么办? 然后可以使用weak_ptr,是的。 但是,随后任何人都可能会收到一个weak_ptr调皮并使其锁定的状态,从而使对象的所有者无法破坏该对象。 有时这可能不是问题,但有时是问题。 例如,当拥有的对象表示必须在特定时间放弃的某些系统资源时。
您可能会说:“好吧,那么您应该确保没有人将weak_ptr保持锁定状态!” 但这从OO设计的角度来看并不理想(因为我认为),因为这会在“所有者”对象和从中获取“ weak_ptr”的任何对象之间建立依赖关系。 您还可以提出以下论点:“您无需返回const引用;您应该确保没有人修改该引用”。
有了Qt,您就有了QPointer,这基本上就是我想要的。 它检查对象是否尚未销毁,但无法防止对象被销毁。 我意识到这不是线程安全的,但是我还是在谈论单个线程的上下文。
那么,为什么C ++ 11没有类似的东西? 我敢肯定,我可以在weak_ptr周围做一个包装,完成我的追求。 但是我想知道我是否会出错。
不。它不存在是因为即使对于单个线程也不安全。 考虑:
void some_function (super_weak_ptr foo)
{
foo->some_function();
}
如果some_function
(通过间接路径)导致对象被破坏会发生什么? 在您说那永远不会发生之前,是的。 例如:
void got_some_data (some_type whatObject, some_other_type whatData)
{
super_weak_ptr object = findObject (whatObject);
if (object)
object->youGotMail (whatData);
}
现在,假设youGotMail
函数意识到该对象现在已经获得了所需的最后数据,并且其工作已经完成,它可能会破坏该对象,并且现在我们在一个不再存在的对象上运行一个函数。
如果要使用原始指针,则知道在哪里可以找到指针。 创建比原始指针更安全的“智能”指针没有多大意义。
因此,如果您不管理对象的生存期,则需要能够锁定该对象,然后才能对该对象执行任何操作。
您可以使用shared_ptr<unique_ptr<T>>
使用纯标准C ++进行此操作。
观察者只收到了shared_ptr<const unique_ptr<T>>
,使他们只能看但不能触摸。 具有非const
智能指针的所有者可以随时在unique_ptr
上调用reset()
来销毁实例。 那时所有观察者还可以看到unique_ptr
已为空。
明显的线程和重新进入警告适用(您需要在每次调用回调后检查unique_ptr
再次具有有效的指针,等等)。
而且,如果应该有多个所有者,则需要做更多的工作。 您将需要一个shared_ptr<T*>
,为观察者提供一个shared_ptr<T* const>
。 还有一个单独的shared_ptr<T>
来管理对象的生存期。 shared_ptr<T*>
将需要在对象的析构函数中手动填充为nullptr
( T*
,而不是shared_ptr
)。
such,没有这样的事情。
在2009年,我玩弄了/探索了这样一个智能指针类型 ,我称之为ZPtr
,我记得这是对该方向一些较早代码的清理工作,因为它支持比标准库的静默处理更好的文件抽象错误处理。 iostreams。 (较早的)想法是在没有可能进行进一步的有意义的操作时通过自我破坏来在周围没有任何僵尸对象,而这需要通过可以检测引用对象是否存在的智能指针进行访问。 显然,这在当时并不是一个好主意,因为我发给DDJ的有关该文章的文章遭到了……的沉默对待。
我认为,既然我们已经在该语言中提供了参数转发支持,那么该指针的时机也许已经到了。 可能会过载甚至更好.
操作员。 但是无论如何,必须非常仔细地选择功能。
尽管命名, std::weak_ptr
并没有真正“锁定”。 如果可能的话,它仅用于获取std::shared_ptr
。 而且std::shared_ptr
随时可以为您提供原始指针。
因此,您可以选择不直接std::weak_ptr
,而是仅提供临时原始指针的包装器。
但是,它不是非常安全的线程,并且与ZPtr
不同,它不会为客户端代码提供有关为什么不再存在引用对象的任何想法(当引用对象不存在时)。 但这可能只是您所需要的。 让我喝点咖啡,然后吃点东西,然后我举一个例子。
例:
#include <memory>
namespace cppx {
using std::shared_ptr;
using std::weak_ptr;
template< class Type >
class Poor_ptr
{
private:
struct Null {};
weak_ptr<Type> weak_p_;
public:
explicit operator bool() const { return not is_null(); }
friend
auto operator==( const Poor_ptr& p, Poor_ptr::Null* )
-> bool
{ return p.is_null(); }
friend
auto operator==( Poor_ptr::Null*, const Poor_ptr& p )
-> bool
{ return p.is_null(); }
friend
auto operator!=( const Poor_ptr& p, Poor_ptr::Null* )
-> bool
{ return not p.is_null(); }
friend
auto operator!=( Poor_ptr::Null*, const Poor_ptr& p )
-> bool
{ return not p.is_null(); }
auto is_null() const
-> bool
{ return (ptr_or_null() == nullptr); }
auto ptr_or_null() const
-> Type*
{
try
{
return weak_p_.lock().get();
}
catch( ... )
{
return nullptr;
}
}
auto ptr() const
-> Type*
{ return weak_p_.lock().get(); }
Poor_ptr( shared_ptr< Type > p )
: weak_p_( p )
{}
};
} // namespace cppx
#include <iostream>
using namespace std;
auto main() -> int
{
cout << boolalpha;
auto p = make_shared<int>( 42 );
cppx::Poor_ptr<int> pp = p;
cout
<< "That " << pp.ptr_or_null() << " is null is "
<< (pp == 0) << ", not " << !!pp << ".\n";
p.reset();
cout
<< "That " << pp.ptr_or_null() << " is null is "
<< (pp == 0) << ", not " << !!pp << ".\n";
}
哦,为了覆盖David Schwartz提到的问题,即对象在某些函数的调用中间消失的问题,您可以提供一个执行函数的成员函数,例如以原始指针作为参数的std::function
,其中确保在调用期间保持引用对象的生命(即通过具有本地std::shared_ptr
)。
然后,客户端代码程序员可以选择是否依赖于被调用函数不会破坏对象的假设,或者使用更安全的回调机制。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.