C、C++、java中函数的参数:传值 or 传引用
概要:
教C语言课的过程中,发现很多学生对函数调用中的参数传递问题很容易糊涂。跟师弟交流的过程中,也发现大家对这个问题理解不够透彻。因此,结合本人自己的理解,将函数调用中的传值与传引用进行分析总结。
一、函数调用中传递参数的类型
传值(pass by value):即形参接收实参传递过来的值,如果是自定义类型,要调用拷贝构造函数。函数处理的是形参的值。
传引用(pass by reference):引用表示与原对象完全相同,是同一个对象。若函数的形参是引用,则实参与形参间不存在参数传递,且函数内对形参的修改就是修改实参,因为它们是引用关系,是同一个对象。
1、C语言中,传递的参数类型只有1个:传值,包括传递普通数值和指针。
2、C++中,传递的参数类型有2个:传值、传引用
3、java中,传递的类型只有1个:传值。
java中传的值包括:基本数据类型和对象,其中对象当做指针看待
三种语言的处理方法相同: 不管传递的是普通变量还是指针,都是传值。对于指针,看函数修改的是指针的值,还是指针所指对象的值就可以了。
二、程序示例
1、C++:传递的是基本数据类型,包括普通数值、指针和引用
#include <iostream> using namespace std; void swap(int a,int b); void swapPoint(int *pa, int *pb); void swapData(int *pa,int *pb); void swapReference(int &a, int &b); int main() { int a = 10; int b = 20; cout<<"main函数中, a= "<<a<<" b= "<<b<<endl; swap(a,b); swapPoint(&a,&b); swapData(&a,&b); swapReference(a,b); cout<<"main函数中, a= "<<a<<" b= "<<b<<endl; return 1; } void swap(int a,int b) { int tmp; tmp = a; a = b; b = tmp; cout<<"swap函数中, a= "<<a<<" b= "<<b<<endl; } void swapPoint(int *pa, int *pb) { int *tmp; tmp = pa; pa = pb; pb = tmp; cout<<"swapPoint函数中, a= "<<*pa<<" b= "<<*pb<<endl; } void swapData(int *pa,int *pb) { int tmp; tmp = *pa; *pa = *pb; *pb = tmp; cout<<"swapData函数中, a= "<<*pa<<" b= "<<*pb<<endl; } void swapReference(int &a, int &b) { int tmp; tmp = a; a = b; b = tmp; cout<<"swapReference函数中, a= "<<a<<" b= "<<b<<endl; }
分析:
void swap(int a,int b)函数,形参a、b分别接受实参传递的值,函数处理的是形参a、b,实现交换形参a、b的值
void swapPoint(int *pa, int *pb)函数,交换的是指针的值,即交换的是形参的指向关系
void swapData(int *pa,int *pb)函数,交换的是指针所指向的对象,即交换的是实参
2、C++:传递的是自定义类型:结构体或者类
class Test { public: int a; Test() { cout<<"Test() 无参构造函数!"<<endl; } Test(int data) { a = data; cout<<"Test(int data) 有参构造函数!"<<endl; } Test(const Test &tmp) { a = tmp.a; cout<<"拷贝构造函数!!"<<endl; } }; void swapClass(Test a, Test b) { Test tmp; tmp = a; a= b; b = tmp; } void swapClassReference(Test &a, Test &b) { Test tmp; tmp = a; a= b; b = tmp; } void swapClassPoint(Test *pa, Test *pb) { Test tmp; tmp = *pa; *pa= *pb; *pb = tmp; } int main() { Test a(10); Test b = 20; swapClass(a,b); // swapClassReference(a,b); swapClassPoint(&a,&b); cout<<a.a<<" "<<b.a<<endl; return 1; }
分析:swapClassReference(Test &a, Test &b)函数,由于是引用,不会出现拷贝构造函数的调用。形参就是实参
swapClass(Test a, Test b)函数,会调用拷贝构造函数,给a,b分配存储空间。函数处理的是新定义的形参变量a、b
3、java中的参数传递:传值
java中出现对象时,把当做指针看待。例如定义了一个类Test,接下来有定义Test tmp ; //类似于c++的指针,没有调用构造函数
若Test testc = new Test(); //调用默认构造函数
public class Test { public int data; public String name; public Test() { System.out.println("调用了无参的构造方法Test() "); } public Test(int data,String name) { this.data = data; this.name = name; System.out.println("调用了有参的构造方法Test(int data,String name) "); } public Test(Test src) { data = src.data; name = src.name; System.out.println("调用了拷贝的构造方法Test(Test src) "); } public String toString() { return "data= "+data+", name= "+name; } public static void swap(Test a, Test b) { Test tmp ; //类似于c++的指针,没有调用构造函数 tmp = a; a = b; b = tmp; } public static void modify(Test a) { a.data += 100; a.name +=" is modified!"; } public static void main(String[] args) { Test testA = new Test(2,"testA"); Test testB = new Test(5,"testB"); Test.swap(testA, testB); Test.modify(testA); System.out.println("testA: "+testA.toString()); System.out.println("testB: "+testB.toString()); } }
分析:
主函数中的swap(Test a, Test b)方法,可以证明java中传递的值,而不是引用
modify(Test a)方法中,之所以可以修改属性的值,是因为修改的是指针所指的对象。
总之,java中出现对象时,把当做指针看待
三、总结
1、 C++中传值(特别是对于自定义类型),会带来拷贝构造函数执行的开销,所以执行效率低一点。
传引用和传指针,不执行拷贝构造函数,效率会高
2、 当用按值传递方式传递或返回一个对象时,编译器会自动调用拷贝构造函数!
3、 当指针作为形参时,最好画出指针所指向的对象。然后分析修改的是指针的值,还是指针所指的对象。
(完)