不同shared_ptr
实例的要点是保证(尽可能)只要 thisshared_ptr
在范围内,它指向的对象仍然存在,因为它的引用计数至少为 1。
Class::only_work_with_sp(boost::shared_ptr<foo> sp)
{
// sp points to an object that cannot be destroyed during this function
}
因此,通过使用对 a 的引用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 的更新
这是一个人为的例子。这是故意简单的,所以错误会很明显。在实际示例中,错误并不那么明显,因为它隐藏在真实细节层中。
我们有一个函数可以在某处发送消息。它可能是一条大消息,因此我们使用 a到一个字符串,而不是使用std::string
可能在传递到多个地方时被复制的a :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!
两次。
现在出现了维护者先生,他查看代码并认为:嘿,那个参数send_message
是 a 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
时永远不会为空send_message
。
正如我所说,在一个简单的示例中,所有代码都如此紧密地结合在一起,很容易找到错误。但在实际程序中,相互持有指针的可变对象之间的关系比较复杂,很容易出错,很难构建必要的测试用例来检测错误。
一个简单的解决方案是,您希望函数能够始终依赖于shared_ptr
非空值,即函数分配其自己的 true shared_ptr
,而不是依赖于对现有 的引用shared_ptr
。
缺点是复制的 ashared_ptr
不是免费的:即使是“无锁”实现也必须使用互锁操作来兑现线程保证。shared_ptr
因此,在某些情况下,可以通过将 a 更改为 a来显着加快程序速度shared_ptr &
。但这并不是可以安全地对所有程序进行的更改。它改变了程序的逻辑含义。
std::string
请注意,如果我们在整个过程中使用而不是,会发生类似的错误std::shared_ptr<std::string>
,而不是:
previous_message = 0;
为了清除信息,我们说:
previous_message.clear();
然后,症状将是意外发送空消息,而不是未定义的行为。额外复制一个非常大的字符串的成本可能比复制 a 的成本要高得多shared_ptr
,因此权衡可能会有所不同。