传值
和java不同,如果不指定传引用或者传地址,方法会把实参复制一份,也就是说main函数中的变量i,和restVal方法中的入参i是两个独立的变量,它们有各自的内存地址。这种方式调用不会对原有变量造成影响。
#include <iostream>
using namespace std;
int resetVal(int i)
{
i = 100;
return i;
}
int main(void)
{
int i = 10, j = 20;
cout << "before reset i:" << i << endl;
cout << "call resetVal returned i:" << resetVal(i) << endl;
cout << "after reset i:" << i << endl;
return 0;
}
output:
before reset i:10
call resetVal returned i:100
after reset i:10
调用的方式和Java调用方法一样,只是参数都是实参的一个副本了,这样的调用并不会对之前的原有的i进行修改。
传引用
刚接触有些疑惑,暂时把它当做原始变量的一个别名就好了。例如 int i = 0; int &j = i; i和j都指向同一个内存地址,好比一个人叫王钢蛋,小名叫蛋子一样,这两个名字都是指向一个实体对象。
#include <iostream>
using namespace std;
int resetVal(int& i) // change int i => int& i
{
i = 100;
return i;
}
int main(void)
{
int i = 10, j = 20;
cout << "before reset i:" << i << endl;
cout << "call resetVal returned i:" << resetVal(i) << endl;
cout << "after reset i:" << i << endl;
return 0;
}
output:
before reset i:10
call resetVal returned i:100
after reset i:100
相比传值的方法,只是改了被调用方法的参数列表中的参数类型,从 int 类型改成了 int&,main函数中的调用没有任何变化。个人理解,restVal方法由参数列表可知,入参是一个引用,会自动复制,也就是说在整个调用栈中,main先入栈,创建了i,调用restVal时再入栈,创建i的别名tmp,和i指向同一内存地址。表明这是一个引用,而不是复制品。从结果可知,原始变量i已经被修改了。
传地址(传指针)
指针和引用类似,都是把内存地址传过去了。只是传递的时候需要使用求址运算符(&)把变量的地址取出来传递过去。传引用的时候不需要。接收部分需要使用解引用的方式,把指向的地址转换成具体指向的对象。
#include <iostream>
using namespace std;
int resetVal(int* i) // declare this is a pointer
{
*i = 100; // deRef and change value
return *i; // return the value , not pointer
}
int main(void)
{
int i = 10, j = 20;
cout << "before reset i:" << i << endl;
cout << "call resetVal returned i:" << resetVal(&i) << endl; // declare pass the address of i
cout << "after reset i:" << i << endl;
return 0;
}
output:
before reset i:10
call resetVal returned i:100
after reset i:100
结果和传引用一样,都是修改了原始的i。
于是有了一个疑问,为什么有指针了还要用引用呢?
因为C++是从C沿袭下来的,并且需要使用操作符重载。下面有原文(原文地址):
Why does C++ have both pointers and references?
C++ inherited pointers from C, so I couldn't remove them without causing serious compatibility problems. References are useful for several things, but the direct reason I introduced them in C++ was to support operator overloading. For example:
void f1(const complex* x, const complex* y) // without references
{
complex z = *x+*y; // ugly
// ...
}
void f2(const complex& x, const complex& y) // with references
{
complex z = x+y; // better
// ...
}
More generally, if you want to have both the functionality of pointers and the functionality of references, you need either two different types (as in C++) or two different sets of operations on a single type. For example, with a single type you need both an operation to assign to the object referred to and an operation to assign to the reference/pointer. This can be done using separate operators (as in Simula). For example:
Ref<My_type> r :- new My_type;
r := 7; // assign to object
r :- new My_type; // assign to reference
Alternatively, you could rely on type checking (overloading).For example:
Ref<My_type> r = new My_type;
r = 7; // assign to object
r = new My_type; // assign to reference