40

我想澄清按值和按引用之间的区别。

我画了一幅画

在此处输入图像描述

因此,对于按值传递,

使用不同的引用创建相同对象的副本,并为局部变量分配新引用,因此指向新副本

如何理解这句话:“如果函数修改了该值,则修改也出现在调用函数的范围内,无论是通过值传递还是通过引用传递”

谢谢!

4

6 回答 6

66

我认为不传达引用传递的含义会产生很多混乱。当有些人说通过引用传递时,他们通常不是指参数本身,而是被引用的对象。其他人说通过引用传递意味着对象不能在被调用者中更改。例子:

struct Object {
    int i;
};

void sample(Object* o) { // 1
    o->i++;
}

void sample(Object const& o) { // 2
    // nothing useful here :)
}

void sample(Object & o) { // 3
    o.i++;
}

void sample1(Object o) { // 4
    o.i++;
}

int main() {
    Object obj = { 10 };
    Object const obj_c = { 10 };

    sample(&obj); // calls 1
    sample(obj) // calls 3
    sample(obj_c); // calls 2
    sample1(obj); // calls 4
}

有些人会声称 1 和 3 是通过引用传递,而 2 是通过值传递。另一组人说除了最后一个之外都是按引用传递,因为对象本身没有被复制。

我想在这里定义我声称通过引用传递的内容。可以在此处找到对其的一般概述:按引用传递和按值传递之间的区别。第一个和最后一个是值传递,中间两个是引用传递:

    sample(&obj);
       // yields a `Object*`. Passes a *pointer* to the object by value. 
       // The caller can change the pointer (the parameter), but that 
       // won't change the temporary pointer created on the call side (the argument). 

    sample(obj)
       // passes the object by *reference*. It denotes the object itself. The callee
       // has got a reference parameter.

    sample(obj_c);
       // also passes *by reference*. the reference parameter references the
       // same object like the argument expression. 

    sample1(obj);
       // pass by value. The parameter object denotes a different object than the 
       // one passed in.

我投票支持以下定义:

当且仅当被调用函数的相应参数具有引用类型并且引用参数直接绑定到参数表达式 (8.5.3/4)时,参数 (1.3.1) 才通过引用传递。在所有其他情况下,我们都必须通过值传递。

这意味着以下内容是按值传递的:

void f1(Object const& o);
f1(Object()); // 1

void f2(int const& i);
f2(42); // 2

void f3(Object o);
f3(Object());     // 3
Object o1; f3(o1); // 4

void f4(Object *o);
Object o1; f4(&o1); // 5

1是按值传递,因为它没有直接绑定。实现可以复制临时文件,然后将该临时文件绑定到引用。2是按值传递,因为实现会初始化文字的临时值,然后绑定到引用。3是传值,因为参数没有引用类型。4出于同样的原因,是按值传递。5是按值传递,因为参数没有引用类型。以下情况通过引用传递(根据 8.5.3/4 和其他规则):

void f1(Object *& op);
Object a; Object *op1 = &a; f1(op1); // 1

void f2(Object const& op);
Object b; f2(b); // 2

struct A { };
struct B { operator A&() { static A a; return a; } };
void f3(A &);
B b; f3(b); // passes the static a by reference
于 2009-01-04T11:36:01.987 回答
9

按值传递时:

void func(Object o);

然后打电话

func(a);

您将Object在堆栈上构造一个,并在其实现中funco. 这可能仍然是一个浅拷贝(内部ao可能指向相同的数据),因此a可能会被更改。但是,如果o是深拷贝a,则a不会改变。

通过引用传递时:

void func2(Object& o);

然后打电话

func2(a);

您只会提供一种新的参考方式a。" a" 和 " o" 是同一个对象的两个名称。更改o内部func2将使这些更改对调用者可见,调用者通过名称“ a”知道对象。

于 2009-01-04T07:21:43.670 回答
6

非常感谢大家的所有这些投入!

我从网上的讲义中引用了这句话: http ://www.cs.cornell.edu/courses/cs213/2002fa/lectures/Lecture02/Lecture02.pdf

第一页第六张幻灯片

" 通过 VALUE 传递变量的值将传递给函数如果函数修改了该值,则修改将保留在该函数的范围内。

通过 REFERENCE 传递 对变量的引用传递给函数 如果函数修改了该值,则修改也会出现在调用函数的范围内。

"

再次非常感谢!

于 2009-01-04T19:59:15.700 回答
5

我不确定我是否正确理解了您的问题。这有点不清楚。但是,可能会让您感到困惑的是以下内容:

  1. 通过引用传递时,对同一对象的引用将传递给被调用的函数。对对象的任何更改都将反映在原始对象中,因此调用者将看到它。

  2. 按值传递时,将调用复制构造函数。默认的复制构造函数只会做浅拷贝,因此,如果被调用函数修改了对象中的整数,调用函数将不会看到,但如果函数更改了对象内指针指向的数据结构, 那么这将被调用者看到由于浅拷贝。

我可能误解了你的问题,但我想无论如何我都会给它一个刺。

于 2009-01-04T07:43:30.817 回答
2

当我解析它时,这些话是错误的。它应为“如果函数修改了该值,则在通过引用传递时,修改也会出现在调用函数的范围内,但在通过值传递时不会出现。”

于 2009-01-04T07:21:13.407 回答
1

我对“如果函数修改了该值,修改也出现在调用函数的范围内,通过值传递和引用传递”这句话的理解是它们是错误的

通过值传递时,在被调用函数中所做的修改不在调用函数的范围内。

要么你打错了引用的词,要么它们是从任何上下文中提取出来的,这似乎是错误的,对。

您能否确保您正确引用了您的来源,如果没有错误,请在源材料中提供更多围绕该声明的文本。

于 2009-01-04T09:15:20.163 回答