传递对std :: shared的引用
如果我有一个需要使用shared_ptr
的函数,将它的引用传递给它会不会更高效(所以为了避免复制shared_ptr
对象)? 什么是可能的不良副作用? 我设想了两种可能的情况:
1)函数内部的副本由参数组成,如in
ClassA::take_copy_of_sp(boost::shared_ptr<foo> &sp)
{
...
m_sp_member=sp; //This will copy the object, incrementing refcount
...
}
2)在函数内部只使用参数,就像
Class::only_work_with_sp(boost::shared_ptr<foo> &sp) //Again, no copy here
{
...
sp->do_something();
...
}
在这两种情况下,我都看不到按值传递boost::shared_ptr<foo>
而不是引用的好理由。 按值传递只会“暂时”增加由于复制引起的引用计数,然后在退出函数作用域时将其递减。 我可以忽略一些东西吗
为了澄清,在阅读了几个答案之后:我完全同意过早优化问题,并且我总是试图首先在那些热点上工作。 如果你知道我的意思,我的问题更多来自纯粹的技术代码观点。
一个独特的shared_ptr
实例的目的是保证(尽可能)只要这个shared_ptr
在范围内,它指向的对象将仍然存在,因为它的引用计数至少为1。
Class::only_work_with_sp(boost::shared_ptr<foo> sp)
{
// sp points to an object that cannot be destroyed during this function
}
因此,通过使用对shared_ptr
的引用,可以禁用该保证。 所以在你的第二种情况下:
Class::only_work_with_sp(boost::shared_ptr<foo> &sp) //Again, no copy here
{
...
sp->do_something();
...
}
你怎么知道sp->do_something()
不会由于空指针而炸掉?
这完全取决于代码的那些'...'部分中的内容。 如果你在第一个'...'中调用了一个清除shared_ptr
到同一个对象的副作用(在代码的另一部分的某处),该怎么办? 如果碰巧是该对象唯一的不同shared_ptr
呢? 再见对象,就在你要尝试和使用它的地方。
所以有两种方法可以回答这个问题:
仔细检查整个程序的来源,直到确定对象在功能体中不会死亡。
将参数更改为独立的对象而不是引用。
此处适用的一般建议:不要为了性能而对代码进行有风险的更改,直到您将您的产品定位到实际情况下的分析器中并确定地测量出您要做的更改将会使性能有显着差异。
更新评论者JQ
这是一个人为的例子。 这是故意的简单,所以错误会很明显。 在实际的例子中,这个错误并不是那么明显,因为它隐藏在真实的细节层中。
我们有一个函数可以在某处发送消息。 它可能是一个很大的消息,而不是使用std::string
,它可能在被传递到多个位置时被复制,所以我们使用shared_ptr
指向一个字符串:
void send_message(std::shared_ptr<std::string> msg)
{
std::cout << (*msg.get()) << std::endl;
}
(我们只是将它“发送”到本例的控制台)。
现在我们想添加一个设施来记住以前的消息。 我们需要以下行为:必须存在一个包含最近发送的消息的变量,但是当消息正在发送时,必须没有先前的消息(该变量在发送之前应该被重置)。 所以我们声明新的变量:
std::shared_ptr<std::string> previous_message;
然后根据我们指定的规则修改我们的函数:
void send_message(std::shared_ptr<std::string> msg)
{
previous_message = 0;
std::cout << *msg << std::endl;
previous_message = msg;
}
因此,在我们开始发送之前,我们放弃当前的消息,然后在发送完成后,我们可以存储新的先前消息。 都好。 以下是一些测试代码:
send_message(std::shared_ptr<std::string>(new std::string("Hi")));
send_message(previous_message);
正如所料,这打印Hi!
两次。
现在来看看Maintainer先生,他看着代码并认为:嗨, send_message
参数是一个shared_ptr
:
void send_message(std::shared_ptr<std::string> msg)
显然可以改为:
void send_message(const std::shared_ptr<std::string> &msg)
想想这会带来的性能增强! (不要介意我们即将通过某个频道发送一个通常较大的消息,因此性能增强将会很小以至于无法衡量)。
但真正的问题是,现在测试代码将显示未定义的行为(在Visual C ++ 2010调试版本中,它崩溃了)。
Maintainer先生对此感到惊讶,但为send_message
添加了一个防御性检查,试图阻止问题的发生:
void send_message(const std::shared_ptr<std::string> &msg)
{
if (msg == 0)
return;
但是它当然会继续并且崩溃,因为调用send_message
时msg
不会为空。
正如我所说,所有的代码在一个简单的例子中如此接近,很容易发现错误。 但是在真正的程序中,在彼此指向的可变对象之间存在更复杂的关系时,很容易犯这个错误,也很难构造必要的测试用例来检测错误。
简单的解决方案是让函数能够依赖于shared_ptr
,并始终保持非空值,这个简单的解决方案是为函数分配自己的真实shared_ptr
,而不是依赖于对现有shared_ptr
的引用。
缺点是复制shared_ptr
不是免费的:即使“无锁”实现也必须使用互锁操作来保证线程保证。 因此,可能会出现这样的情况,即通过将shared_ptr
更改为shared_ptr &
来显着提高程序的速度。 但这并不是一个可以安全地对所有程序做出的改变。 它改变了程序的逻辑意义。
请注意,如果我们在整个过程中使用std::string
而不是std::shared_ptr<std::string>
,则会发生类似的错误,而不是:
previous_message = 0;
为了清除这个信息,我们说:
previous_message.clear();
那么症状将是意外发送空的消息,而不是未定义的行为。 一个非常大的字符串的额外副本的成本可能比复制一个shared_ptr
的成本要重要得多,所以权衡可能不同。
我发现自己不同意最高票数的答案,所以我去寻找专家的意见,他们在这里。 来自http://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2011-Scott-Andrei-and-Herb-Ask-Us-Anything
Herb Sutter:“当你通过shared_ptr时,副本很贵”
Scott Meyers:“关于shared_ptr没有什么特别的地方,它涉及到是否通过价值传递它,或者通过引用传递它。使用与用户定义的其他类型完全相同的分析。人们似乎认为shared_ptr可以解决某些问题所有的管理问题,因为它很小,所以通过价值传递肯定是低成本的,它必须被复制,并且与此相关的成本......通过价值传递它是昂贵的,所以如果我可以逃避它在我的程序中有适当的语义,我会通过引用const或引用来传递它“
Herb Sutter:“总是通过引用const来传递它们,偶尔也许是因为你知道你所说的可能会修改你从中获得参考的东西,也许那么你可能会传递值...如果你将它们作为参数复制,哦我的天哪,你几乎从不需要碰到引用计数,因为它无论如何都是活着的,而且你应该通过引用来传递它,所以请做那个“
更新:Herb在这里扩展了这个:http://herbsutter.com/2013/06/05/gotw-91-solution-smart-pointer-parameters/,虽然故事的寓意是你不应该通过shared_ptr“,除非你想使用或操纵智能指针本身,比如分享或转让所有权。”
我会建议反对这种做法,除非你和你一起工作的其他程序员真的知道你在做什么。
首先,你不知道你的类的接口可能会如何演变,并且你想阻止其他程序员做坏事。 通过引用传递shared_ptr并不是程序员期望看到的东西,因为它不是惯用的,这使得它很容易错误地使用它。 程序防御性:使界面难以正确使用。 通过引用传递会在稍后引发问题。
其次,除非你知道这个班级将会成为一个问题,否则不要进行优化。 首先是配置文件,然后如果你的程序真的需要通过引用传递给予的提升,那么也许。 否则,不要为了小数据而花费大量的时间(即额外的N个指令来传递值),而是担心设计,数据结构,算法和长期可维护性。
链接地址: http://www.djcxy.com/p/20773.html上一篇: passing references to std::shared
下一篇: How do we use a abstract class as a function return type?