按值、常量值、引用或常量引用传递非 POD 类型

Posted

技术标签:

【中文标题】按值、常量值、引用或常量引用传递非 POD 类型【英文标题】:Passing non-POD type by value, const value, reference or const reference 【发布时间】:2014-03-03 05:20:51 【问题描述】:

我需要将非 POD 类型传递给 C++ 函数。 我想修改该函数内部的值,但我不希望该更改在该函数之外可见。

我的第一个选择是按值传递,这会创建一个副本。

void myFunction (NonSimpleObject foo) 
  foo = ~foo;

我也可以通过引用传递,这在函数调用过程中速度更快,但我需要在内部创建一个副本以不影响外部值;

void myFunction (NonSimpleObject &foo) 
  NonSimpleObject foo_internal = ~foo;      

为了向调用者发出我不会修改外部值的信号,我想包含一个 const 限定符。这在按值调用时当然是隐含的,但我想更详细一点。但是传递一个 const 值将迫使我在内部创建第二个副本来修改该值,这也与 const 限定符最初的用途相反。

void myFunction (const NonSimpleObject foo) 
  NonSimpleObject foo_internal = ~foo;

传递一个 const 引用既会向调用者发出信号,表明外部值没有改变,并且只需要在函数内部复制一份。

void myFunction (const NonSimpleObject &foo) 
  NonSimpleObject foo_internal = ~foo;      

哪一个最适合我的目的(良好的性能,对调用者来说很冗长),优点/缺点是什么?

这也归结为以下问题:在函数内部进行复制而不是在参数传递期间进行复制是否有任何优势,反之亦然?

【问题讨论】:

传递(非常量)值似乎是最干净的方法,我认为没有缺点(如果您决定函数根本不需要副本,则重新编译除外)。 按值传递,并在堆栈副本上进行更改,这些将在您的函数之外不可见。 To signal to the caller that I will not modify the outside value, I would like to include a const qualifier. This is of course implicit when calling by value, but I would like to be more verbose 这太傻了,原因你在下一行找到。 【参考方案1】:

只需按值传递即可:

它会创建一个副本(让您无需为其编写任何代码,即显式副本) 关于您对所做操作的语义的担忧:通过值传递的参数向调用代码清楚地表明您没有修改函数内部的对象,您创建它的副本,并且原始对象将保持不变。 const 更好地用于您传递给函数的实际对象(即带有引用)并且您希望它不被修改(例如,只是为了“窥视”它)。 通过引用传递需要您手动创建一个副本,这是通过值传递来完成的。

所以,这样:

void myFunction (NonSimpleObject foo) 
   //modify foo here

【讨论】:

无法修改对对象的常量引用【参考方案2】:

第一种情况 (void myFunction (NonSimpleObject foo)) 将创建两个对象。第一个对象是foo,第二个是foo_internal

第二种情况 (void myFunction (const NonSimpleObject &foo)) 将只创建一个对象 - 即 foo_internal

因此,第二种情况更好。

在函数内部进行复制而不是在参数传递期间进行复制有什么好处,反之亦然?

如果您要使用创建的对象,请复制它。您没有显示足够的代码来判断是否需要副本。就目前而言 - 我会说使用 const 引用。

【讨论】:

第一种情况其实是void myFunction (NonSimpleObject foo),这是最好的选择。 通过 const 引用获取它然后在函数内部进行显式复制将禁止利用移动语义。因此,我会坚持简单的价值传递。 @juanchopanza doh 不知何故我错过了那个。无论如何,他正在创建另一个像 NonSimpleObject foo_internal = ~foo; 这样的对象。【参考方案3】:

如果你需要一个本地对象,那么按值传递是最好的。如果参数是临时的或显式移动的,这将启用移动语义,从而可以避免不必要的复制。

无论是否需要,通过引用传递都会强制复制。如果引用不是const,则参数不能是临时的。按值接受const 对象然后复制它是很奇怪的。

(请注意,在您的具体示例中,您不需要本地副本,只需将运算符应用于参数的结果;因此 const 引用可能更合适。)

【讨论】:

【参考方案4】:

“我想修改该函数内部的值,但我不希望该更改在该函数之外可见。”

那么您的意思是更改对象的本地副本。 (我认为您的意思不是 friend-function,它允许您更改对象的私有数据。)

所以您需要按值传递,最干净的解决方案是您的 1.(而不是通过引用,然后在本地副本中制作(您的 2.解决方案))。

这向用户发出信号,传递的对象不会被更改(只有它的本地副本,但这就是你想要的)。将其按值传递给 const(您的 3. 解决方案)略有不同:您不仅向用户发出信号,传递的对象不会被更改,而且本地副本是不可更改的 .

通过 const 引用传递它表示传递的对象既不会被更改,也不存在可更改的本地副本。

所以你应该考虑一下你真正想要什么。

【讨论】:

以上是关于按值、常量值、引用或常量引用传递非 POD 类型的主要内容,如果未能解决你的问题,请参考以下文章

通过值或常量引用传递给函数?

Java的参数传递是「按值传递」还是「按引用传递」?

按引用传递或按值传递指针容器之间的区别

如何通过值或常量引用传递 std::string_view

C99 常量值传递

Objective-C 对象是按值传递还是按引用传递?