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

87

这里有一个简单的问题,我在这里找不到答案。

据我所理解,在函数调用时传递参数,例如:

void myFunction(type myVariable)
{
}

void main()
{
    myFunction(myVariable);
}

对于像intfloat等简单数据类型,函数是通过值调用的。

但是如果myVariable是一个数组,则仅传递起始地址(尽管我们的函数是按值调用的函数)。

如果myVariable是一个对象,则仅传递对象的地址,而不是创建一个副本并传递它。

那么回到问题。C ++是通过引用或值传递对象吗?


10
@juanchopanza 发布了一份书单??:0 严肃点吧,为什么要踩他呢?我得去买所有这些书才能知道问题的答案。我很抱歉但感到沮丧。 - user3041058
6
总的来说,这不是一个坏问题。但是它表明您可能应该花点时间学习语言的基础知识,并通过几个测试案例进行实验。如果您这样做,您会很快发现传递对象的默认行为是复制。您可以通过传递 std::vector<int> myVariable 参数,在 myFunction 中添加并在那里打印它,然后在 main 中打印它来看到这一点。修改将不会反映在 main 的输出中。但是 C++ 可以超越这些传统;这是它的疯狂和魅力的一部分。阅读相关内容,不要绝望! - HostileFork says dont trust SE
5
我的书没有清楚地解释对象的传递,以及数组的传递方式。我感到困惑。我在谷歌上搜索了很多次,但都没找到一个通用的答案,所有的问题都是具体的。无法得到满意的回答,因此我决定询问。 - user3041058
12
我们能否对新用户友善些?这里的一些人总是成群结队地出现,但不用担心他们。 - jww
4
我认为这是一个非常合理和有用的问题。我有几本关于C++的书,已经使用了将近5年(主要是为了完成学校作业),但我并不能总是记得所有不同语言在最低级别上如何操作。在工作中,我使用C#和一点Python,家里的随机项目使用C++,上周开始我认真地学习C。它们都有一些不同之处,我需要一个快速简便的方法来在C++方面进行复习,而不必翻阅400-600页的书籍。 - RTHarston
显示剩余4条评论
5个回答

78

除非函数签名另有规定,否则参数按值传递:

  • void foo(type arg) 中,无论 type 是简单类型、指针类型还是类类型,arg 都是按值传递的。
  • void foo(type& arg) 中,arg 是按引用传递的。

对于数组,传递的值是一个指向数组第一个元素的指针。如果在编译时知道数组的大小,也可以通过引用来传递数组:void foo(type (&arg)[10])


3
在讨论这个问题时,我认为我们应该谨慎使用术语。传值意味着“您得到的是参数的值”,而传引用意味着“您得到的是参数的引用/别名”,与编译器实际执行的操作(复制、移动、省略复制、传递指针、传递引用等)无关。我发表这篇评论是因为我认为将“传值”与“复制”以及“传引用”与“不复制”等同起来是人们常见的误解之源,从C(没有传递引用,使用指针模拟)开始,一直延续到C++和Java。 - Manu343726
@Manu343726,C语言没有传递引用的方式。为什么?如果传递引用意味着传递地址,那么C语言为什么没有传递引用的方式?我认为传递引用与引用变量无关,而是与通过指针或引用变量传递地址有关。 - a Learner
1
@aLearner 按引用传递意味着创建一个对已存在对象的别名。因此,当您使用“int & a = x”时,您正在为x创建新名称。这样做的主要原因是在修改a时更改x的值。在C中,没有办法向同一现有对象添加新名称,因此我们取一个对象的地址并将其传递,以便我们可以更改原始变量的值。使用指针实际上是通过值进行复制。当您使用“int * a =&x”时,您正在复制地址的值。这就是Manu343726使用指针来模拟引用的含义。 - Mandar Sadye

30
C++总是给你选择:所有类型T(除了数组,见下文)可以通过将参数类型设为T来按值传递,并通过将参数类型设为T&,即对T的引用来传递。
当参数类型没有明确注释为引用(type &myVariable),无论具体类型如何,它都会被视为按值传递。对于用户定义的类型也是如此(这就是复制构造函数的作用)。对于指针也是如此,即使复制指针并不会复制指向的内容。
数组有点更复杂。数组不能按值传递,像int arr[]这样的参数类型实际上只是int *arr的不同语法。不是将数组传递给函数产生指针,而是几乎每个可能的操作(仅排除一些操作,如sizeof)都会这样做。一个人可以传递一个对数组的引用,但这需要显式注释为引用:int (&myArray)[100](注意符号&)。

2
+1 是为了指出 int arr[] 实际上只是 int *arr。这使得传递数组的值似乎与其他类型 T 不同,因为实际上它正在做你如果写 int *arr 的话所期望的事情。 - RTHarston
@RTHarston arr[] 不是 *arrint arr[] 声明并为一个数组分配空间,arr 是第一个元素的地址,而不是指针。int *arr2 声明并为一个 int 的指针(或者是一个数组的第一个元素)分配空间。指针可以接收一个数组的地址,arr2 = arr,但是反过来是不可能的 arr = ...。一个参数 arr 只是一个地址,而在 arr2 的情况下,指针的内容被传递。 - Déjà vu
@Déjàvu 感谢分享。我知道 arr[]*arr 不是同一回事,三年前我就知道了,所以我不确定我在之前的评论中到底想表达什么。arr[] 可以自动转换为 *arr,但这并不意味着它们是相同的东西。我想知道我当时是否在考虑汇编级别的问题?类型系统知道 arr[]*arr 是不同的,但在汇编中,当调用方法时,它们可能看起来很相似,因为只传递了指向数组基址的指针。 - RTHarston
在函数的参数列表中,int arr[]int *arr相同类型。在其他上下文中,它们是不同的。怪罪C语言。 - undefined

9

C++ 可以同时实现按值传递和按引用传递。

下面是两个使用示例。

http://www.learncpp.com/cpp-tutorial/72-passing-arguments-by-value/

http://www.learncpp.com/cpp-tutorial/73-passing-arguments-by-reference/

数组是特殊的构造,当您将数组作为参数传递时,会将指向第一个元素地址的指针作为该元素类型的值传递。

当您将指针作为参数传递时,实际上是自己实现了按引用传递的范例,就像在 C 中一样。因为当您修改指定地址中的数据时,您确切地修改了调用函数中的对象。


0
在C++中,声明为类、结构体或联合体的类型被认为是“类类型”。这些类型通过值传递,或者可以说是通过复制构造函数传递给函数。当我们实现二叉树时,几乎总是在递归函数中使用Node *类型的参数来操作二叉树。这样做是为了方便修改该节点。如果节点按原样传递(即不是指针类型),则对节点的修改将仅限于本地副本。即使在向量的情况下,也会将向量的副本传递给函数,为了避免这种情况,我们使用引用&。

-6

C++通过值传递非指针(int*)或引用(int&)的参数。您无法在被调用的函数中修改调用块的变量。数组是指针。


3
C++ 也通过值来传递指针。没有任何神奇的例外。 - juanchopanza
1
在C++术语中,参数类型是指针并不影响它是按值传递还是按引用传递。如果它只是一个指针,则按值传递。如果它是指向指针的引用,则按引用传递。 - Joseph Mansfield
1
我的意思是,参数声明为指针或引用。对不起,我的英语不好。我的理解是:“我能在被调用的函数中修改传递的变量吗?” - scraatz
5
如果参数声明为指针,则指针按值传递。如果声明为指针的引用,则按引用传递。另外,数组不是指针。 - juanchopanza
数组退化为指针。可以参考https://dev59.com/NlLTa4cB1Zd3GeqPWgQI。 - jww
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接