我有一些代码使用shared_ptr
作为在我的应用中引用特定类型对象(让我们称之为T
)的标准方式。我试图谨慎使用make_shared
和std::move
以及const T&
来提高效率。尽管如此,我的代码花费了大量时间来传递shared_ptr
(我在shared_ptr
中包裹的对象是整个caboodle的中心对象)。踢球者通常shared_ptr
指向一个用作标记的对象,没有价值&#34 ;;这个对象是特定T
子类的全局实例,它永远存在,因为它的引用计数永远不会为零。
使用"无价值"对象很好,因为它以很好的方式响应发送到这些对象的各种方法,以我想要的方式运行"没有价值"表现。但是,性能指标表明我的代码中花费了大量时间来增加和减少该全局单例对象的引用计数,使新的shared_ptr
引用它然后销毁它们。也就是说:对于一个简单的测试用例,如果我将nullptr
卡在shared_ptr
内以表示"没有值"而不是制作,则执行时间从9.33秒变为7.35秒他们指向全球单身人士T
"没有价值"宾语。这是一个非常重要的区别;运行在更大的问题上,这段代码将很快用于在计算集群上进行多日运行。所以我真的需要加速。但我真的很想拥有自己的价值#34;对象也是如此,所以我不必在我的代码中检查nullptr
,特殊情况下这种可能性。
因此。有没有办法让我的蛋糕也吃?特别是,我想象我可能以某种方式将shared_ptr
子类化为" shared_immortal_ptr"我可以使用"没有价值"宾语。子类的行为就像普通shared_ptr
一样,但它根本不会递增或递减其引用计数,并且会跳过所有相关的簿记。这样的事情有可能吗?
我还考虑制作一个内联函数,在我的get()
上执行shared_ptr
,如果get()
返回nullptr
,则会将指针替换为单例不朽对象1}};如果我在代码中的任何地方使用它,并且从未直接在我的*
上使用->
或shared_ptr
,我想我会被隔离。
还是有另一个很好的解决方案可以解决这种情况吗?
答案 0 :(得分:1)
Galik询问了有关遏制策略的核心问题。我假设您已经考虑过并且有理由依赖shared_ptr作为一种无法替代的共同遏制策略。
我有些建议似乎有争议。您定义的是您需要一种从不具有nullptr的shared_ptr类型,但std::shared_ptr
没有这样做,我检查了各种版本的STL以确认提供的客户删除器不是解决方案的切入点。
因此,请考虑制作自己的智能指针,或采用您更改的指针以满足您的需求。基本思想是建立一种shared_ptr,可以指示它将阴影指针指向它不拥有的全局对象。
您拥有std::shared_ptr
的来源。阅读代码令人不舒服。可能很难使用。这是一条途径,但当然您要复制源代码,更改命名空间并实现您想要的行为。
您可以实现shared_ptr的基本界面来创建替换。如果你很好地使用了typedef,那么你应该改变一些有限的地方,但至少搜索和替换会合理地运作。
这是我建议的两种方式,两者都以相同的功能结束。要么从库中采用shared_ptr,要么从头开始创建。你可以快速取得替代品,这让你感到惊讶。
如果采用std::shared_ptr
,主要的主题是理解shared_ptr如何确定它应该减少。在大多数实现中,shared_ptr必须引用一个节点,在我的版本中它调用一个控制块(_Ref)。当引用计数达到零时,节点拥有要删除的对象,但如果_Ref为null,则自然shared_ptr会跳过该对象。但是,运营商喜欢 - >和*,或者get函数,不要再检查_Ref,它们只是返回阴影,或者是我的版本中的_Ptr。
现在,当调用重置时,_Ptr将被设置为nullptr(或在我的源中为0)。分配给另一个对象或指针时会调用Reset,因此即使使用赋值给nullptr也可以。关键是,对于您需要的这种新类型的shared_ptr,您可以简单地更改行为,以便每当发生这种情况时(重置为nullptr),您将_Ptr(shared_ptr中的阴影指针)设置为"无值全球"对象的地址。
*,get或 - >的所有用途将返回没有值对象的_Ptr,并且当在另一个赋值中使用时将正确地运行,或者再次调用reset,因为这些函数不依赖于阴影指针作用于节点,并且因为在此特殊情况下如果节点(或控制块)将为nullptr,则shared_ptr的其余部分将表现为正确指向nullptr - 即不删除全局对象。
显然,将std::pointer
更改为此类特定于应用程序的行为听起来很疯狂,但坦率地说,这是性能工作往往使我们做的事情;其他奇怪的事情,比如偶尔放弃C ++以获得更快的C或汇编程序的速度。
修改std::shared_ptr
来源,作为此特殊目的的副本,不是我选择的(事实上,我已经面对你的情况的其他版本,所以我做了几个这样的选择几十年的时间。)
为此,我建议你构建一个基于策略的智能指针。我觉得奇怪,我今天在另一篇文章(或昨天,凌晨1点40分)提出这个问题。
我指的是2001年的Alexandrescu的书(我认为它是现代C ++ ......还有一些我不记得的词)。他提出了loki,其中包括基于策略的智能指针设计,该设计仍在其网站上发布并免费提供。
在我看来,这个想法应该被纳入shared_ptr。
基于策略的设计是作为模板类的范例实现的,该模板类源自一个或多个参数,如下所示:
template< typename T, typename B >
class TopClass : public B {};
通过这种方式,您可以提供B,从中构建对象。现在,B可能具有相同的结构,它也可能是一个策略级别,它源自它的第二个参数(或多个派生,但设计有效)。
可以组合图层以实现各种类别中的独特行为。
例如:
std::shared_ptr
和std::weak_ptr
是单独的类,它们作为一个系列与其他人(节点或控制块)进行交互以提供智能指针服务。但是,在我多次使用的设计中,这两个是由相同的顶级模板类构建的。该设计中shared_ptr和weak_ptr之间的区别在于模板的第二个参数中提供的附件策略。如果使用弱附加策略作为第二个参数来实例化类型,则它是弱指针。如果它有一个强大的附件政策,它就是一个智能指针。
创建策略设计模板后,您可以引入不在原始设计中的图层(展开它),或者引入&#34;拦截&#34;行为并将其专门化为您当前所需的行为 - 而不会破坏原始代码或设计。
我开发的智能指针库具有很高的性能要求,以及许多其他选项,包括自定义内存分配和自动锁定服务,以使写入智能指针线程安全(std::shared_ptr
无法提供)。接口和大部分代码是共享的,但是可以通过选择不同的策略来简化几种不同类型的智能指针。要更改行为,可以插入新策略而不更改现有代码。目前,我使用std :: shared_ptr(我在几年前使用它时)和我多年前开发的MetaPtr库,后者当我需要高性能或灵活的选项时,就像你的那样。
如果std :: shared_ptr是基于策略的设计,正如loki所示,您可以使用shared_ptr执行此操作,而无需复制源并将其移动到新的命名空间。
无论如何,只需创建一个共享指针,该指针在重置为nullptr时指向全局对象的阴影指针,使节点指向null,提供您描述的行为。