c++: Loki StrongPtr 对我来说看起来不安全,是这样吗?
Posted
技术标签:
【中文标题】c++: Loki StrongPtr 对我来说看起来不安全,是这样吗?【英文标题】:c++: Loki StrongPtr looks unsafe to me, is that so? 【发布时间】:2011-08-17 14:05:38 【问题描述】:我目前正在研究最流行的智能 Ptr 实现,例如 boost 共享指针和弱指针以及 loki Smart and Strong pointer,因为我想实现自己的并且据我所知 Loki 强指针对我来说看起来不安全,但我宁愿认为我理解错了,所以我想讨论一下它是否安全。我认为它不安全的原因是,据我所知,它没有足够小心地对待弱指针(即 StrongPtr,其中 false 表示其弱):
例如解引用函数:
PointerType operator -> ()
KP::OnDereference( GetPointer() ); //this only asserts by default as far as i know
//could be invalidated right here
return GetPointer();
在多线程环境中,弱指针可能随时失效,因此该函数可能会返回一个无效的 Ptr。
据我了解,您要么必须创建要取消引用的 ptr 的 strongPtr 实例,以确保它不会在中途失效。我认为这也是 boost 不允许您在不先创建 shared_ptr 实例的情况下取消引用 weak_ptr 的原因。我认为 Lokis StrongPtr 构造函数遇到了同样的问题。
这是一个问题还是我读错了 src?
【问题讨论】:
它看起来确实不安全,特别是因为如果不创建强指针,对象不仅可以在您返回它之前被删除,而且在用户使用它时也会被删除(哎哟)。也许是多线程很难的一个很好的例子! 好吧,你要指出的是,它不是 thread 安全的。我不知道它是否曾经承诺过是线程安全的(从你展示的 sn-p 来看,它看起来不像)。 (不相关:您的问题标题完全是修辞和自我指涉) 我认为你对课堂的要求有点过分。没有锁定,您无法保证您不拥有的东西不会发生邪恶的事情。无论您应该在 operator-> 中添加什么更好的检查以使实际的取消引用完全安全,在您取消引用指针并且仍在成员函数调用中之后,它仍然可能被破坏。就是这样。 你的观点当然是正确的,但一方面 loki 强指针带有多线程感知引用计数,这根本无法解决我指出的问题。实际上,我的问题并不是真正的修辞,因为如上所述,我很困惑,一方面它意味着在使用 MT 感知引用计数时是线程安全的,而显然不是。我虽然我错过了一些东西:) 【参考方案1】:仔细阅读后,我想我明白了其中的道理。
StrongPtr
对象是双重的,因为它们代表 Strong
和 Weak
引用。
assert
的机制在Strong
版本上运行良好。在Weak
版本上,调用者有责任确保引用的对象能够存活足够长的时间。这可以实现:
Strong
版本
手动,通过创建Strong
实例
std::shared_ptr
的好处是,当您已经知道该项目将超过您的使用期限时,您可以避免创建新对象。这是一个值得商榷的设计决定,但对专家来说非常有用(Alexandrescu 无疑就是其中之一)。它可能不是针对普通用户(我们),因此强制采用Strong
版本会更好恕我直言。
也有人会争辩说,事后看来,批评总是更容易。 Loki
,尽管很伟大,但已经过时了。
【讨论】:
问题在于,StrongPtr 的设计(顺便说一下,它是 Rich Sposato 在 2006 年编写的),您甚至无法安全地从弱版本创建强版本。 【参考方案2】:关于assert
的使用,在一个空的StrongPtr<>
实例上使用operator->
是一个编程错误;即,调用者有责任确保StrongPtr<>
实例在取消引用之前不为空。为什么需要assert
以外的任何东西?也就是说,如果您认为其他一些行为比assert
更合适,那么这正是政策的目的。
这是前置条件和后置条件的根本区别;这是关于这个主题的一个很长但非常好的主题:comp.lang.c++.moderated: Exceptions。特别阅读 D. Abrahams 的帖子,因为他详细解释了我所说的理解事实。 ;-]
关于 StrongPtr<>
的线程安全,我怀疑 Loki 的大部分内容都早于任何严重的线程安全问题;另一方面,boost::shared_ptr<>
和 std::shared_ptr<>
明确保证是线程安全的,所以我确信它们的实现为研究提供了一个“更好”(虽然更复杂)的基础.
【讨论】:
我不同意,正如您所看到的,断言根本不足以确保上述情况下的任何事情。之前检查引用计数也不会改变任何东西。如前所述,据我所知,在多线程环境中您唯一能做的就是手动保留引用计数,或者在取消引用弱 ptr 之前制作强副本。我很确定您无法通过检查策略安全地解决问题,因为您必须返回某种在内部存储强指针以保留引用计数的代理对象。 @user300713 :重点是,assert
不是为了确保任何事情——它是为了提醒程序员确保某事(StrongPtr<>
非空)。同样,在空的StrongPtr<>
实例上使用operator->
是一个编程错误,并且不存在可以防止编程错误的机制。你能做的最好的事情是使用一些简单的东西,让所说的程序员知道他们有一个编程错误,最好以一种避免降低运行时性能的方式 - 即assert
。
好吧,我从来没有说过 assert 可以确保任何事情。我只是说,在我刚刚概述的情况下,它甚至可能不会让程序员知道任何事情。
如果另一个线程在我使用它时消除了最后一个 StrongPtr,它会在哪里出现编程错误?这肯定是单线程设计中的编程错误,但我认为在多线程情况下没有任何责任,因为正如我概述了 StrongPtr 的设计方式(但也许我错了),你作为使用它的程序员不能做任何关于它的破坏。
@user300713 :确实如此,但我们偏离了实际点:StrongPtr<>
是不不安全的,因为程序员有责任确保 @987654337 @instance 在取消引用之前是非空的。因此,取消引用 empty StrongPtr<>
会调用未定义的行为——你可能会得到一个断言,你可能会得到内存损坏,谁知道呢?这就是UB的本质。如果您的论点是 assert
可能不足以报告由于多线程上下文中可能的竞争条件而导致的编程错误,那么我同意。 ;-]以上是关于c++: Loki StrongPtr 对我来说看起来不安全,是这样吗?的主要内容,如果未能解决你的问题,请参考以下文章
LOKI C++:“ScatterHierarchyTag”如何解决继承歧义?
CUDAMemcpy 对我来说毫无意义......为啥我要在普通 C++ 中指定设备内存?