如果我声明一个包装在共享指针中的对象:
std::shared_ptr<myClass> myClassObject(new myClass());
然后我想将它作为参数传递给方法:
DoSomething(myClassObject);
//the called method
void DoSomething(std::shared_ptr<myClass> arg1)
{
arg1->someField = 4;
}
以上只是增加了shared_pt的引用计数,一切都很酷吗?还是留下一个悬垂的指针?
你还是应该这样做吗?:
DoSomething(myClassObject.Get());
void DoSomething(std::shared_ptr<myClass>* arg1)
{
(*arg1)->someField = 4;
}
我认为第二种方式可能更有效,因为它只需复制1个地址(而不是整个智能指针),但第一种方式似乎更具可读性,我不期望推动性能限制。我只是想确保没有危险的东西。
谢谢。
答案 0 :(得分:152)
我想将共享指针传递给函数。你能帮帮我吗?
当然,我可以帮助你。我假设您对C ++中的所有权语义有一定的了解。这是真的吗?
是的,我对这个问题感到相当满意。
好。
好的,我只能考虑采用shared_ptr
论证的两个理由:
shared_ptr
s。你对哪一个感兴趣?
我正在寻找一般答案,所以我实际上对两者都感兴趣。我很好奇你在#2的情况下的意思。
此类函数的示例包括std::static_pointer_cast
,自定义比较器或谓词。例如,如果您需要从向量中找到所有唯一的shared_ptr,则需要这样的谓词。
啊,当函数实际需要操作智能指针本身时。
完全。
在这种情况下,我认为我们应该通过引用传递。
是。如果它不改变指针,你想通过const引用传递。由于您不需要共享所有权,因此无需复制。那是另一种情况。
好的,明白了。我们来谈谈另一种情况。
您分享所有权的那个?好。您如何与shared_ptr
分享所有权?
复制它。
然后该函数需要复制shared_ptr
,正确吗?
显然。所以我通过引用const传递它并复制到局部变量?
不,这是一种悲观情绪。如果通过引用传递,该函数将别无选择,只能手动进行复制。如果按值传递,编译器将在副本和移动之间选择最佳选择并自动执行。所以,按值传递。
好点。我必须经常记住“Want Speed? Pass by Value.”文章。
等等,如果函数将
shared_ptr
存储在成员变量中,该怎么办?这不会成为冗余副本吗?
该函数可以简单地将shared_ptr
参数移动到其存储中。移动shared_ptr
很便宜,因为它不会改变任何引用计数。
啊,好主意。
但我正在考虑第三种情况:如果您不想操纵
shared_ptr
,也不想分享所有权该怎么办?
在这种情况下,shared_ptr
与函数完全无关。如果你想操纵指针,请指点,并让调用者选择他们想要的所有权语义。
我应该通过引用还是通过值来引用指针?
通常的规则适用。智能指针不会改变任何东西。
如果我要复制,则按值传递,如果我想避免复制,则按引用传递。
右。
啊,一个有趣的边缘案例。我不希望经常发生这种情况。但是当它发生时你可以通过值传递并在不需要时忽略副本,或者通过引用传递并在需要时制作副本。嗯。我想你又忘了另一个场景。如果我想分享所有权,但仅取决于某种条件,该怎么办?
我在第一个选项中冒了一个冗余副本,在第二个选项中丢失了一个潜在的移动。我不能吃蛋糕吗?
如果你处于真正重要的情况,你可以提供两个重载,一个采用const左值引用,另一个采用右值引用。一个副本,另一个移动。完美转发功能模板是另一种选择。
我认为这涵盖了所有可能的情景。非常感谢你。
答案 1 :(得分:17)
我认为人们不必担心将原始指针用作函数参数。如果函数不会存储指针或以其他方式影响其生命周期,则原始指针也可以正常工作并表示最小公分母。例如,考虑如何将unique_ptr
传递给一个函数,该函数将shared_ptr
作为参数,通过值或const引用?
void DoSomething(myClass * p);
DoSomething(myClass_shared_ptr.get());
DoSomething(myClass_unique_ptr.get());
作为函数参数的原始指针不会阻止您在调用代码中使用智能指针,这非常重要。
答案 2 :(得分:3)
是的,关于shared_ptr&lt;&gt;的整个想法是多个实例可以保持相同的原始指针,只有在最后一个shared_ptr&lt;&gt;的实例时才会释放底层内存。被毁了。
我会避免指向shared_ptr&lt;&gt;的指针因为你现在正在处理raw_pointers,这就失败了。
答案 3 :(得分:2)
第一个例子中的值传递是安全的,但有一个更好的习语。尽可能通过const引用 - 即使在处理智能指针时也会说是。你的第二个例子并没有被完全打破,但它非常!???
。愚蠢,没有完成任何事情并且击败了智能指针的一部分,当你试图取消引用和修改事物时,会让你陷入一个充满痛苦的痛苦世界。
答案 4 :(得分:0)
在您的职能中
DoSomething
您正在更改类myClass
的实例的数据成员
因此,您要修改的是托管(原始指针)对象而不是(shared_ptr)对象。这意味着在该函数的返回点,所有指向托管原始指针的共享指针都将看到其数据成员:myClass::someField
更改为其他值。
在这种情况下,您将对象传递给函数,并确保您没有修改它(谈论shared_ptr而不是拥有的对象)。
表达这一点的惯用法是通过:const ref,就像这样
void DoSomething(const std::shared_ptr<myClass>& arg)
同样,您要确保函数的用户不会将其他所有者添加到原始指针的所有者列表中。但是,您可以修改原始指针指向的基础对象。
CAVEAT:这意味着,如果以某种方式有人在调用函数之前调用了shared_ptr::reset
,那时候它是拥有raw_ptr的最后一个shared_ptr,那么您的对象将被销毁,并且您的函数将进行操作指向被破坏对象的悬空指针。非常危险!!!