不同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
,我们使用a shared_ptr
来表示字符串,而不是使用可能会在传递到多个位置时被复制的a:
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调试版本中,它会崩溃)。
维护者先生对此感到惊讶,但send_message
为阻止问题的发生添加了防御性检查:
void send_message(const std::shared_ptr<std::string> &msg)
{
if (msg == 0)
return;
但是当然它仍然会继续前进并崩溃,因为调用msg
时永远不会为null send_message
。
正如我所说,在一个简单的示例中,所有代码如此紧密地结合在一起,很容易发现错误。但在实际方案,持有指向对方可变对象之间的关系更复杂,很容易做出错误,并努力构建必要的测试用例来检测错误。
一个简单的解决方案shared_ptr
是让函数分配自己的true shared_ptr
,而不是依赖现有的引用,从而使函数能够始终依赖于非null shared_ptr
。
缺点是复制a shared_ptr
不是免费的:即使“无锁”实现也必须使用互锁操作来遵守线程保证。因此,在某些情况下,可以通过将a更改shared_ptr
为a 来大大加快程序运行速度shared_ptr &
。但这不是可以安全地对所有程序进行的更改。它改变了程序的逻辑含义。
请注意,如果我们在全文中使用std::string
而不是和std::shared_ptr<std::string>
,则将发生类似的错误:
previous_message = 0;
为了清除消息,我们说:
previous_message.clear();
症状是意外发送空消息,而不是不确定的行为。额外复制非常大的字符串的成本可能比复制a的成本要重要得多shared_ptr
,因此权衡可能会有所不同。