如何将智能指针传递给函数?

Posted

技术标签:

【中文标题】如何将智能指针传递给函数?【英文标题】:How do I pass smart pointers into functions? 【发布时间】:2012-09-13 05:50:49 【问题描述】:

将对象传递给函数时,是否适用于智能指针和其他包含动态内存的对象一样的规则?

例如,当我将std::vector<std::string> 传递给函数时,我总是会考虑以下选项:

    我要更改矢量对象的状态,但我希望在函数完成后反映这些更改,AKA 复制。

    void function(std::vector<std::string> vec);
    

    我要更改矢量对象的状态,并且我确实希望在函数完成后反映这些更改,AKA 做一个参考。

    void function(std::vector<std::string> & vec);
    

    这个对象很大,所以我最好传递一个引用,但是告诉编译器不要让我改变它。

    void function(std::vector<std::string> const& vec);  
    

现在这与智能指针的逻辑相同吗?我什么时候应该考虑移动语义?一些关于我应该如何传递智能指针的指南是我最想要的。

【问题讨论】:

unique_ptr 不可复制,您必须手动进行复制。 std::unique_ptr&lt;T&gt;(mew T(*otherptr));除此之外应该和向量一样。 见cpp-next.com/archive/2009/08/want-speed-pass-by-value和***.com/questions/8114276/… 移动语义是怎么回事?”这是一个完全不同的问题,已经被提出并回答了。 【参考方案1】:

智能指针具有指针语义,而不是值语义(嗯,不是你的意思)。将shared_ptr&lt;T&gt; 视为T*;像这样对待它(好吧,除了引用计数和自动删除)。复制智能指针不会复制它指向的对象,就像复制T* 不会复制它指向的T

您根本无法复制unique_ptr。这个类的重点是它不能被复制;如果可以,那么它就不是指向对象的 unique(即:单数)指针。您必须通过某种形式的引用或通过移动它来传递它。

智能指针都是关于它们所指对象的所有权。谁拥有此内存,谁将负责删除它。 unique_ptr 代表唯一所有权:只有一段代码拥有该内存。您可以转让所有权(通过move),但这样做会失去内存的所有权。 shared_ptr 代表共享所有权。

在所有情况下,在参数列表中使用智能指针表示转让所有权。因此,如果一个函数采用智能指针,那么它将声明该对象的所有权。如果一个函数不应该拥有所有权,那么它根本不应该使用智能指针;使用引用 (T&amp;) 或者如果您需要可空性,则使用指针但永远不要存储它。

如果您将unique_ptr 传递给某人,则表示您授予他们所有权。这意味着,根据唯一所有权的性质,您将失去内存的所有权。因此,除了值之外,几乎没有理由通过任何方式传递unique_ptr

同样,如果你想共享某个对象的所有权,你可以传入shared_ptr。你是通过参考还是通过价值来做这件事取决于你。由于您共享所有权,因此无论如何它都会复制(大概),因此您不妨按价值接受它。该函数可以使用std::move将其移动到类成员等中。

【讨论】:

所以,只是一个愚蠢的问题:如果您通过值传递 unique_ptr 会发生什么?它不是复制它,这违反了它的唯一性吗?或者您的意思是说unique_ptr 只能通过引用传递? @MihaiTodor:必须是doSomenthing(std::move(x));;你不能隐式移动左值。 即使函数没有所有权,我也不建议使用哑指针。最好始终使用智能指针。如果所有权不受影响,只需通过const&amp; 传递它们。这将使您的代码更加万无一失,并避免编写类似void f(T*); f(new T()); 的内容。 @Rost: +1 避免裸指针。然而,在许多情况下,如何管理对象的生命周期与函数无关。该函数应该只接受一个T&amp;,如果调用者实际上是一个(智能)指针,则调用者可以取消引用。 @Deduplicator:好的,某些 API 采用可能为空的指针是完全有效的。但坦率地说,它 比函数毫无疑问需要一个真实对象的情况少见。因此,在可能的情况下更倾向于参考的一般建议最终仍然有效。【参考方案2】:

如果函数不打算修改或复制指针,只需使用哑指针代替。智能指针用于控制对象的生命周期,但该函数不会更改生命周期,因此它不需要智能指针,并且使用哑指针可以为调用者使用的类型提供一些灵活性。

void function(std::string * ptr);

function(my_unique_ptr.get());
function(my_shared_ptr.get());
function(my_dumb_ptr);

unique_ptr 不能被复制,所以如果你必须传递它,你必须传递一个引用。

【讨论】:

-1: factual error: "so if you must pass it you must pass a reference." 你可以进入参数。 @NicolBolas:但是如果被移动了,对象就会被销毁,这样会报错。 @Deduplicator:请参阅我关于如何传递智能指针的帖子。或者更重要的是,你怎么不这样做。 @NicolBolas:也许我今天有点慢,但我没有得到你想要指出的内容。这个答案的重点似乎是,如果该参数仅用于访问指针而不是转移所有权,则要求任何智能指针类型都太麻烦了,而应该使用原始(观察)指针。您的第一条评论说,可以改为进入智能指针参数。这会转移所有权,对我来说似乎是错误的。 @NicolBolas 这是真的,你不能,除非你打算改变指针的所有权。阅读答案的第一句话,了解它所依赖的资格。【参考方案3】:

智能指针是一个引用另一个对象并管理其生命周期的对象。

传递智能指针需要尊重智能指针支持的语义:

作为const smartptr&lt;T&gt;&amp; 传递始终有效(您无法更改指针,但可以更改其指向的状态)。 以smartptr&lt;T&gt;&amp; 传递始终有效(您也可以更改指针)。 仅当 smartptr 可复制时,以smartptr&lt;T&gt; 的形式传递(通过复制)才有效。它适用于std::shared_ptr,但不适用于std::unique_ptr,除非您在调用时“移动”它,就像在func(atd::move(myptr)) 中一样,从而使myptr 无效,将指针移动到传递的参数。 (请注意,如果 myptr 是临时的,则移动是隐含的)。 作为smartptr&lt;T&gt;&amp;&amp; 传递(通过移动)通过强制您显式使用std::move(但需要“移动”以使特定指针有意义)来强制在调用时移动指针。

【讨论】:

"pssign as smartptr&& (by move)" && 并不意味着 move。在非模板推导参数参数中使用右值引用意味着只能向函数传递可以绑定到右值引用的表达式。所以无论是临时的还是从函数返回的T&amp;&amp;。在构造对象之前不会发生实际运动。 @NicolBolas:这正是我的概念:通过强制使用 std::move 来“强制移动”,否则函数不会绑定。如果我改写可能会更好...... -1:事实错误:“passign as smartptr(通过复制)仅在 smartptr 可复制时才有效”这根本不是真的。按值传递对于仅移动类型非常有效。你只需要进入争论。 @NicolBolas:请理解我的回答的语义遵循问题之一,而不是 C++ 规范,它使用相反的方式来阐述概念。正如您所说,“按值传递对于仅移动类型来说效果很好。您只需要进入参数即可。”但是在你这样做之后,你还没有得到一个副本(周围没有两个等于指针)。当我说 copy 时,我的意思是英语词典(不是 C++ 规范)为 copy 定义的内容。 @NicolBolas:我不能对(还)不知道的人使用“定义明确的术语”!我必须使用正常的日常语言说话,否则我会通过编写代码而不是文本来回应。如果那是你的口味,那就保持吧。但要明白这是你对我的“品味”。这背后没有科学依据。我认为自己很幸运没有你作为我的老师。【参考方案4】: 我知道问这个问题的人比我在 C++ 方面知识渊博,并且这个问题有一些完美的答案,但我相信这个问题最好以一种不会吓到 C++ 的人的方式回答,尽管它可以有点复杂,这是我的尝试:

考虑到 C++ 中只有一种智能指针,它是 shared_ptr,所以我们有以下选项可以将它传递给函数:

1 - 价值void f(std::shared_ptr&lt;Object&gt;);

2 - 参考void f(std::shared_ptr&lt;Object&gt;&amp;);

最大的不同是,第一个借给所有权和第二个让你操纵所有权。

进一步阅读和详细信息可以在this link 之前帮助过我。

【讨论】:

以上是关于如何将智能指针传递给函数?的主要内容,如果未能解决你的问题,请参考以下文章

将智能指针传递给函数

将这个包装在智能指针中的问题传递给 C++ 中的方法

将包装在智能指针中的问题传递给C ++中的方法的问题

函数中的智能指针

智能指针总结

C++ 中带智能指针的依赖注入