提问人:Steve H 提问时间:5/31/2012 最后编辑:R. Martinho FernandesSteve H 更新时间:6/16/2023 访问量:98826
将共享指针作为参数传递
Passing shared pointers as arguments
问:
如果我声明一个包装在共享指针中的对象:
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 个地址(而不是整个智能指针),但第一种方法似乎更具可读性,我预计不会突破性能限制。我只是想确保它没有危险的东西。
谢谢。
答:
是的,关于shared_ptr<>的整个想法是,多个实例可以保存相同的原始指针,并且只有当shared_ptr<>的最后一个实例被销毁时,底层内存才会被释放。
我会避免指向shared_ptr<>的指针,因为这会破坏目的,因为您现在再次处理raw_pointers。
在第一个示例中按值传递是安全的,但有一个更好的习惯用语。如果可能的话,通过常量引用传递 - 即使在处理智能指针时,我也会说是。你的第二个例子并没有完全坏,但它非常.愚蠢,什么都没做,打败了聪明指针的部分意义,当你试图取消引用和修改事物时,会让你陷入一个痛苦的错误世界。!???
评论
shared_ptr<>
shared_ptr<>
shared_ptr
shared_ptr<> const &
shared_ptr
const
shared_ptr<>
shared_ptr<>
我想将共享指针传递给函数。你能帮我吗?
当然,我可以帮你。我假设您对 C++ 中的所有权语义有一定的了解。这是真的吗?
是的,我对这个主题相当满意。
好。
好吧,我只能想到两个理由来争论:shared_ptr
- 该函数希望共享对象的所有权;
- 该函数执行一些专门针对 s 的操作。
shared_ptr
你对哪一个感兴趣?
我正在寻找一个一般的答案,所以我实际上对两者都感兴趣。不过,我很好奇你在案例 #2 中的意思。
此类函数的示例包括 、自定义比较器或谓词。例如,如果需要从向量中查找所有唯一shared_ptr,则需要这样的谓词。std::static_pointer_cast
啊,当函数实际需要操作智能指针本身时。
完全。
在这种情况下,我认为我们应该通过引用。
是的。如果它不更改指针,则希望通过常量引用进行传递。无需复制,因为您不需要共享所有权。这是另一种情况。
好的,知道了。让我们谈谈另一种情况。
你分享所有权的那个?还行。您如何与?shared_ptr
通过复制它。
那么函数就需要做一个副本,对吗?shared_ptr
明显地。所以我通过引用 const 传递它并复制到局部变量?
不,这是一种悲观。如果它是通过引用传递的,则该函数别无选择,只能手动进行复制。如果它是按值传递的,编译器将在复制和移动之间选择最佳选择并自动执行。因此,按值传递。
好点子。我必须记住“想要速度吗?按值传递。文章更频繁。
等等,例如,如果函数将 存储在数据成员中呢?那不会成为多余的副本吗?
shared_ptr
该函数可以简单地将参数移动到其存储中。移动 a 很便宜,因为它不会更改任何引用计数。shared_ptr
shared_ptr
啊,好主意。
但我正在考虑第三种情况:如果你不想操纵 ,也不想分享所有权怎么办?
shared_ptr
在这种情况下,与函数完全无关。如果要操作指针,请取一个指针,并让调用方选择他们想要的所有权语义。shared_ptr
我应该通过引用还是按值来获取指针?
通常的规则适用。智能指针不会改变任何内容。
如果我要复制,则按值传递,如果我想避免复制,则按引用传递。
右。
嗯。我想你忘记了另一种情况。如果我想分享所有权,但只取决于某个条件怎么办?
啊,一个有趣的边缘案例。我不认为这种情况会经常发生。但是,当它发生时,你可以按值传递,如果你不需要它,可以忽略它,或者通过引用传递,如果你需要它,可以制作副本。
我冒着在第一个选项中一个冗余副本的风险,而在第二个选项中会失去一个潜在的移动。我不能把蛋糕也吃吗?
如果您处于真正重要的情况下,可以提供两个重载,一个采用常量左值引用,另一个采用右值引用。一个复制,另一个移动。完美转发函数模板是另一种选择。
我认为这涵盖了所有可能的情况。谢谢。
评论
shared_ptr<T> ptr;
void f(T&)
f(*ptr)
void f(T)
void f(T&)
我认为人们不必要地害怕使用原始指针作为函数参数。如果函数不打算存储指针或以其他方式影响其生存期,则原始指针同样有效,并表示最低公分母。例如,考虑如何将 a 传递给一个将 a 作为参数的函数,无论是通过值还是通过 const 引用?unique_ptr
shared_ptr
void DoSomething(myClass * p);
DoSomething(myClass_shared_ptr.get());
DoSomething(myClass_unique_ptr.get());
将原始指针作为函数参数不会阻止您在调用代码中使用智能指针,而智能指针确实很重要。
评论
DoSomething(*a_smart_ptr)
fun(&x)
fun(x)
x
nullptr
在函数中,您正在更改类实例的数据成员,因此您要修改的是托管(原始指针)对象,而不是 (shared_ptr) 对象。这意味着在此函数的返回点,指向托管原始指针的所有共享指针都将看到其数据成员:更改为不同的值。DoSomething
myClass
myClass::someField
在这种情况下,您将一个对象传递给一个函数,并保证您不会修改它(谈论shared_ptr而不是拥有的对象)。
表达这一点的成语是通过:一个 const ref,就像这样
void DoSomething(const std::shared_ptr<myClass>& arg)
同样,您向函数的用户保证,您不会将另一个所有者添加到原始指针的所有者列表中。但是,您保留了修改原始指针指向的基础对象的可能性。
注意:这意味着,如果有人在你调用函数之前通过某种方式调用,并且此时它是拥有raw_ptr的最后一个shared_ptr,那么你的对象将被销毁,你的函数将操纵一个悬空的指针到被销毁的对象。非常危险!!shared_ptr::reset
评论
const std::shared_ptr<myClass>& arg1
DoSomething
void DoSomething(myClass& arg1)
shared_ptr<>
std::make_shared
std::shared_ptr