3种传参方式:值传参、指针传参、引用传参
我们知道传参方式有3种:值传参、指针传参、引用传参。这三种方式书写方式各有不同,接下来,我们简单介绍3种传参方式实现的机制。首先看一个程序:这个程序实现的是交换2个变量的值。
#include <iostream>
//交换函数
void val_swap(int a, int b); //值传参
void ptr_swap(int *a, int *b); //指针传参
void ref_swap(int &a, int &b); //引用传参
int main(int argc, char* argv[])
{
int a = 10, b = 20;
std::cout << "值传参前,a = " << a << ", b = " << b << std::endl;
val_swap(a,b);
std::cout << "值传参后,a = " << a << ", b = " << b << std::endl;
std::cout << std::endl;
std::cout << "指针传参前,a = " << a << ", b = " << b << std::endl;
ptr_swap(&a, &b);
std::cout << "指针传参后,a = " << a << ", b = " << b << std::endl;
std::cout << std::endl;
std::cout << "引用传参前,a = " << a << ", b = " << b << std::endl;
ref_swap(a, b);
std::cout << "引用传参后,a = " << a << ", b = " << b << std::endl;
std::cout << std::endl;
getchar();
return 0;
}
void val_swap(int a, int b) {
int temp = a;
a = b;
b = a;
}
void ptr_swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
void ref_swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
实现结果:
- 值传参并没有实现交换功能。我们可以通过反汇编查看一下它的内部实现机制。汇编代码如下。
31: int temp = p;
01301D1E mov eax,dword ptr [p]
01301D21 mov dword ptr [temp],eax
32: p = q;
01301D24 mov eax,dword ptr [q]
01301D27 mov dword ptr [p],eax
33: q = temp;
01301D2A mov eax,dword ptr [p]
01301D2D mov dword ptr [q],eax
从汇编指令可以看出,函数值传参是把所传的值压栈,然后赋值给形参,例如主函数中的a传给p时,先将a压栈,然后将这个栈地址所指向的值赋值给p。这样操作的话,就是局部变量之间的交换,而不是外部变量的交换,所以值传参交换失败。
2.指针传参
36: int temp = *p;
01301C9E mov eax,dword ptr [p]
01301CA1 mov ecx,dword ptr [eax]
01301CA3 mov dword ptr [temp],ecx
37: *p = *q;
01301CA6 mov eax,dword ptr [p]
01301CA9 mov ecx,dword ptr [q]
01301CAC mov edx,dword ptr [ecx]
01301CAE mov dword ptr [eax],edx
38: *q = temp;
01301CB0 mov eax,dword ptr [q]
01301CB3 mov ecx,dword ptr [temp]
01301CB6 mov dword ptr [eax],ecx
指针传参是将该实参的地址传给形参,在调用的过程中,通过地址去寻找相应值进行交换,所以指针传参交换成功。
3. 引用传参
42: int temp = p;
01301CDE mov eax,dword ptr [p]
01301CE1 mov ecx,dword ptr [eax]
01301CE3 mov dword ptr [temp],ecx
43: p = q;
01301CE6 mov eax,dword ptr [p]
01301CE9 mov ecx,dword ptr [q]
01301CEC mov edx,dword ptr [ecx]
01301CEE mov dword ptr [eax],edx
44: q = temp;
01301CF0 mov eax,dword ptr [q]
01301CF3 mov ecx,dword ptr [temp]
01301CF6 mov dword ptr [eax],ecx
引用传参与指针传参实现的机制完全相同,这涉及到引用的本质是指针常量。但是引用在传参方面有更简洁,不会出现书写的错误。
常量引用传参
由上面我们可以得知,普通的指针传参和引用传参的实现机制是完全相同的,但是引用传参书写更加简洁。
引用有一个特殊的传参方式:常量引用传参,该方式用于在函数体内部不想改变形参的值。
比如:
Void func(const int &a);
这样传参的方式,函数体内部不能够改变变量a的值,而且这样写的程序更加规范,如果该函数体是这样写的:
Void func(int &a);
那么,如果我有一个const int a的值要传给这个函数将会报错。
传值与传引用的效率对比
请见博客:http://www.cnblogs.com/QG-whz/p/5129173.html
总结:
- 传值调用(Call by Value):函数(方法)在调用参数时,不是对原参数进行操作,而是创建参数的拷贝并对其进行操作,即只是利用它来进行其他计算而不改变其值。
- 引用是独占的,即一旦创建了引用并初始化为某特定对象,它将总是引用此对象,给引用赋值并不会使它“指向”另外的对象,只是改变了对象的值。
传地址:是传值的一种特殊方式,只是它传递的是地址,不是普通的如int 。那么传地址以后,实参 和行参都指向同一个对象。
以下是必须使用引用传参的情况:
1:当大型的类对象必须作为参数传递时对实际的应用程序而言分配对象并拷贝到栈中的时间和空间开销往往过大.
2:当实参的值必须被修改时,例如在函数swap()中用户想改变实参的值但是在按值传递的情况下无法做到.- 使用常量引用的情况: 在函数体内部不想改变形参的值
- 对内置类型来说,通常传值更高效。对用于自定义类型来所,传值要经历构造与析构过程,一般比较耗时