「传递引用」这个词汇在C和C++开发者中都被使用,但似乎它们所表示的意义不同。那么这个模糊的短语在每种语言中确切的区别是什么?
「传递引用」这个词汇在C和C++开发者中都被使用,但似乎它们所表示的意义不同。那么这个模糊的短语在每种语言中确切的区别是什么?
有些问题已经涉及到按值传递和按引用传递的区别。实质上,将参数按值传递给函数意味着该函数将拥有其自己的参数副本-它的值被复制了。修改该副本不会修改原始对象。但是,当按引用传递时,在函数内部的参数指向传入的相同对象-函数内部的任何更改都将在外部可见。
不幸的是,“按值传递”和“按引用传递”这两个短语的使用方式存在两种可能会引起混淆的情况。我认为这在某种程度上是导致新的C++程序员难以采用指针和引用的原因,特别是当他们来自C背景的时候。
在C中,严格来说,所有的东西都是按值传递的。也就是说,无论你将什么作为函数的参数传递,都会被复制到该函数中。例如,调用一个带有void foo(int)
签名的函数并使用foo(x)
作为参数,将会把x
的值复制到foo
的参数中。这可以在一个简单的例子中看到:
void foo(int param) { param++; }
int main()
{
int x = 5;
foo(x);
printf("%d\n",x); // x == 5
}
x
的值被复制到foo
中,并且该副本被增加。在main
中的x
仍然保持其原始值。int* p
将p
定义为指向int
的指针。重要的是要注意,以下代码引入了两个对象:int x = 5;
int* p = &x;
第一个变量的类型是int
,其值为5
。第二个变量的类型是int*
,它的值是第一个对象的地址。
当向函数传递指针时,仍然按值传递。它所包含的地址被复制到函数中。在函数内部修改该指针不会改变函数外部的指针 - 但是,在函数内部修改它指向的对象将会改变函数外部的对象。但是,为什么呢?
由于具有相同值的两个指针总是指向相同的对象(它们包含相同的地址),因此可以通过两个指针访问和修改指向的对象。这赋予了通过引用传递指向对象的语义,尽管实际上根本没有引用 - 在C语言中根本不存在引用。看一下更改后的示例:
void foo(int* param) { (*param)++; }
int main()
{
int x = 5;
foo(&x);
printf("%d\n",x); // x == 6
}
当我们把int*
传递给函数时,可以说指向的int
是“按引用传递”的,但实际上int
根本没有被传递到任何地方-只有指针被复制到函数中。这给了我们“按值传递”和“按引用传递”的口语意义。
这种术语的使用得到了标准术语的支持。当您有指针类型时,它所指向的类型称为其“引用类型”。也就是说,int*
的引用类型是 int
。
指针类型可以来自函数类型、对象类型或不完全类型,称为“引用类型”。
虽然在标准中,一元运算符(如*p
)被称为间接运算,但通常也被称为指针的解除引用。这进一步促进了在C中“按引用传递”的概念。
C ++从C采用了许多原始语言特性,其中包括指针,因此仍然可以使用这种口语化的“按引用传递”形式-*p
仍然是解除引用p
。但是,使用这个术语会很困惑,因为C ++引入了C没有的功能:真正传递引用的能力。
一个类型后面跟着一个&是一个引用类型2。例如,int&
是对int
的引用。当将参数传递给接受引用类型的函数时,对象确实通过引用传递。没有指针参与,没有对象复制,没有任何东西。函数内部的名称实际上指的是与传入的对象完全相同的对象。为了与上面的例子形成对比:
void foo(int& param) { param++; }
int main()
{
int x = 5;
foo(x);
std::cout << x << std::endl; // x == 6
}
现在 foo
函数有一个参数,它是对一个 int
的引用。现在当传递 x
时,param
引用的是完全相同的对象。递增 param
对 x
的值产生了可见的改变,现在 x
的值为6。int*&
)。但是,在使用指针时,您可能会遇到“按引用传递”的用法,但现在您至少知道实际发生了什么。public void foo(Bar param) {
param.something();
param = new Bar();
}
如果你在Java中调用这个函数并传入类型为Bar
的某个对象,那么对param.something()
的调用将在你传入的同一对象上进行。这是因为你传递了一个指向你的对象的引用。然而,即使新的Bar
被分配给param
,函数外部的对象仍然是同一个旧对象。外部永远无法看到新对象,因为在foo
内部的引用被重新分配到了一个新对象上。这种重新分配引用的行为在C++引用中是不可能实现的。
1“口语化”并不意味着C语言中的“按引用传递”的含义比C++含义更不准确,只是因为C++确实有引用类型,所以你真正地通过引用进行参数传递。C语言中的含义是在“按值传递”的抽象之上。
2当然,这些是左值引用,现在在C++11中还有右值引用。