在 C++ 中按引用和值传递

Pass by reference and value in C++

提问人:user36064 提问时间:1/4/2009 最后编辑:Peter Mortensenuser36064 更新时间:8/16/2022 访问量:110965

问:

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

我画了一幅画:

Enter image description here

因此,为了按值传递,使用不同的引用创建相同对象的副本,并为局部变量分配新的引用,以便指向新副本

我应该如何理解以下内容?

如果函数修改了该值,则修改也会显示 在调用函数的范围内,用于按值传递和按引用传递

C++ 按引用 传递值

答:

2赞 dreeves 1/4/2009 #1

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

11赞 Flame 1/4/2009 #2

按值传递时:

void func(Object o);

然后打电话

func(a);

您将在堆栈上构造一个,并在该堆栈的实现中由 引用。这可能仍然是一个浅拷贝(内部可能指向相同的数据),因此可能会更改。但是,如果是 的深层副本,则不会更改。Objectfuncoaoaoaa

通过引用传递时:

void func2(Object& o);

然后打电话

func2(a);

你只会提供一种新的参考方式。“” 和 “” 是同一对象的两个名称。在内部进行更改将使调用方看到这些更改,调用方知道对象的名称为“”。aaoofunc2a

6赞 vboctor 1/4/2009 #3

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

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

  2. 按值传递时,将调用复制构造函数。默认的复制构造函数将只执行浅层复制,因此,如果被调用函数修改了对象中的整数,则调用函数不会看到这一点,但如果函数更改了对象中指针指向的数据结构,则调用方将看到浅层复制。

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

1赞 jwpfox 1/4/2009 #4

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

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

要么你打错了引用的单词,要么它们被从任何上下文中提取出来,使看起来是错误的,对的。

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

68赞 Johannes Schaub - litb 1/4/2009 #5

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

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是按值传递的,因为它不是直接绑定的。实现可以复制临时引用,然后将该临时引用绑定到引用。 是按值传递的,因为实现初始化文本的临时值,然后绑定到引用。 是按值传递的,因为参数没有引用类型。 出于同样的原因,是按值传递的。 是按值传递的,因为参数没有引用类型。以下情况通过引用传递(根据 8.5.3/4 和其他规则):2345

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

评论

1赞 yesraaj 1/8/2009
你能解释一下“引用参数直接绑定到参数表达式”吗?
0赞 Johannes Schaub - litb 1/8/2009
如果参数的类型不同,或者不是参数的派生类,并且没有参数类型的转换运算符,则参数表达式不会直接绑定到引用参数。
0赞 Johannes Schaub - litb 1/8/2009
此外,如果参数是右值(如示例中的整数文本 42)。您可以在标准中找到详细的定义
0赞 1/9/2009
在“void f1(Object const&o)中;f1(Object());“ 为什么允许 impl 复制临时的?
0赞 Johannes Schaub - litb 1/9/2009
Iraimbilanja,因为标准是这样说的(阅读 8.5.3p5)。允许实现创建副本,但不必这样做。事实上,当将右值(Object())传递给Object的复制构造函数时,它不能这样做,以防止无限递归(它需要再次复制:p)