看了几篇值传递和引用传递的好文章,特此汇总后转发出来,希望给迷途的小羔羊们一些帮助,分割线代表不同但是联系十分紧密的文章,有方法有实例,大家好好看看
一直分不清楚他们是干什么的,这回好好的总结下:
1:按值传递:
程序如下:
- #include <iostream>
- using namespace std;
- void swap(int a,int b)//指针保存地址
- {
- int c;
- cout<<"前: a:"<<a<<" b:"<<b<<endl;
- c=a;
- a=b;
- b=c;
- cout<<"后: a:"<<a<<" b:"<<b<<endl;
- }
- int main()
- {
- int a=3,b=4;
- cout<<"主程序前: a:"<<a<<" b:"<<b<<endl;
- swap(a,b);//取地址传递 变量的内存地址
- cout<<"主程序后: a:"<<a<<" b:"<<b<<endl;
- return 0;
- }
输出的结果是:
主程序前: a:3 b:4
前: a:3 b:4
后: a:4 b:3
主程序前: a:3 b:4
主程序的值没有改变,改变的值是栈中的a 和b的副本的值,值传递的时候在栈中拷贝一个变量的副本来操作,这样的话就改变不了以前的值.用的时候要小心.
2:按址传递:
这个时候是用地址传递 程序如下:
- #include <iostream>
- using namespace std;
- void swap(int *a,int *b)//指针保存地址
- {
- int c;
- cout<<"前: a:"<<*a<<" b:"<<*b<<endl;
- c=*a;
- *a=*b;
- *b=c;
- cout<<"后: a:"<<*a<<" b:"<<*b<<endl;
- }
- int main()
- {
- int a=3,b=4;
- cout<<"主程序前: a:"<<a<<" b:"<<b<<endl;
- swap(&a,&b);//取地址传递 变量的内存地址
- cout<<"主程序后: a:"<<a<<" b:"<<b<<endl;
- return 0;
- }
这个就是按地址传递 在方法swap(&a,&b);的参数中&a ,&b都是取地址 那么传递过去的地址由指针来接收 所以在定义函数的时候有void swap(int *a,int *b)定义了俩个指针来存放地址,这样就好理解了 swap(){}函数里面的操作都是按指针来操作的*a ,*b
这样的话结果就是:
主程序前: a:3 b:4
前: a:3 b:4
后: a:4 b:3
主程序前: a:4 b:3
可以看到结果是改变了,因为直接操作的是a,b的内存地址
3:按别名(引用)传递:
- #include <iostream>
- using namespace std;
- void swap(int &a,int &b)//接收俩个别名
- {
- int c;
- cout<<"前: a:"<<a<<" b:"<<b<<endl;
- c=a;
- a=b;
- b=c;
- cout<<"后: a:"<<a<<" b:"<<b<<endl;
- }
- int main()
- {
- int a=3,b=4;
- cout<<"主程序前: a:"<<a<<" b:"<<b<<endl;
- swap(a,b);//直接传递a b 传递过去的时候由别名接收 那么就是自身的别名了
- cout<<"主程序后: a:"<<a<<" b:"<<b<<endl;
- return 0;
- }
这样的话 接受的参数就是俩个别名,他们分别是主函数中a b的别名,因为别名和自身一样 所以结果就改变了
主程序前: a:3 b:4
前: a:3 b:4
后: a:4 b:3
主程序前: a:4 b:3
可以看到用值传递一不小心就达不到目的了,用地址传递的话写法会比较困难,不小心会出错,用引用传递的话考虑的问题就没有那么多了,我们就可以放心的使用了,写函数的时候用引用,那么调用的时候就不用考虑太多了.
**********************************************
在C++中按值传递对象时,会调用拷贝构造函数生成对象的副本,那么对应的C#中也是这样的吗?
无论是在C++中还是在C#中,当变量或对象作为函数参数进行传递时都有两种方式:按值传递和按引用传递。
所谓按值传递是指在函数体内部使用的是对象的副本,在C++中这个副本是调用对象的拷贝构造函数完成的,而函数对副本的修改不会影响原来的对象。如
//C++
void Fun1(Student ss)
{
... ... //对ss进行处理和修改――实际处理的是传入对象的副本
}
... ...
Student s7 ;
Fun1(s7) ;//此函数调用结束后,对象s7的状态并没有改变
... ...
所谓按引用传递是指传给函数的实际上是对象的地址,这样函数对对象的修改就会反应在对象中,使对象的状态发生变化。如
//C++
void Fun2(Student& ss)
{
... ... //对ss进行处理和修改
}
... ...
Student s8 ;
Fun2(s8) ;//此函数调用结束后,对象s8的状态发生改变
... ...
在C++中,可以通过指针和“&”引用符实现引用传递。上面的例子用到了“&”引用符号,其实换成指针也可以达到同样的效果。如果我们再进一步去想,可以发现,当用指针进行引用传递时,也发生了复制,只不过复制的是指针的值(即对象的地址),而不是复制指针指向的对象。这可以通过如下例子得到证明。
//C++
void Fun3(Student* ss)
{
... ... //对ss指向的对象进行处理和修改
ss = NULL ;
}
... ...
Student* s9 ;
Fun3(s9) ;//此函数调用结束后, s9指向的对象的状态发生了改变
... ...
但是在Fun3(s9)调用完后,s9并不是NULL ,这说明Fun3中使用的是指针s9的副本。如果再进一步,我们可以猜测用“&”符实现引用传递时也发生了同样的故事。事实上也是这样,C++中的引用只是一个受限却更加安全的指针而已。
那么按引用传递和按值传递各有什么好处了?
按引用传递不需要发生拷贝行为,因此速度快,特别是大对象时,这种优势很明显。按值传递时对传入对象的修改实际是对对象副本的修改,不会影响原对象的状态。
你也许会想到如果采用const引用传递那么就可以得到双倍的好处,可以这么说,但是不要走极端。
一般而言,将不允许改变的大对象作为const引用传递给函数是很合适的,但如果是简单类型或自定义的小对象直接用值传递就可以了。
如果外界一定要看到函数对对象的修改,那么只有一条路 ―― 按引用传递。
在C#中情况却发生了变化,C#中的引用类型的对象都是按引用传递且只能按引用传递。而值类型对象(或者称为变量),通常情况下是按值传递的。如果要按引用传递值类型对象,那么就要使用关键字ref或out 。ref和out的唯一区别是ref用修饰参数时要求传入的变量被初始化过。
由于类是引用类型,而所有的引用类型的对象的传递都是引用传递,所以在此过程中根本不会发生拷贝函数的调用。照这样看来,根本就没有必要有拷贝构造函数了。
我想现在你已经知道了C# 中为什么不需要拷贝构造函数和很少调用赋值运算符了。你也许会问既然是很少调用赋值运算符,那一定还有调用赋值运算符的情况存在,那么这种情况是怎样的?那是因为类的相仿体――结构struct 。