参数传递的三种方式:
- 按值传递:在调用函数传递参数时,编译器会自动在栈中创建该参数的拷贝,然后将此拷贝传递给被调用函数,故对被调用函数的参数改变只是改变了拷贝,而无法改变主调函数的参数本身的值。
- 按址传递:在调用函数传递参数时,传递参数的地址,这样指针寻址之后修改的也就是参数本神的值。
- 按别名传递:在调用函数传递参数时,传递参数的别名,这样对别名的修改也是对参数本身的修改。
按值传递:
#include <iostream>using namespace std;void swap(int a,int b){int c;cout<<"swap函数中,交换前,a:"<<a<<"b:"<<b<<endl;c=a;a=b;b=c;cout<<"swap函数中,交换后,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;}
缺点:内存开销很大。
对于传递一般参数:
如果传递的参数量过多,例如8000个参数,则需要在栈中复制8000个参数传递过去,栈只有2M的空间,这样的话栈的空间就无法得到充分的利用。
对于传递对象:
按址传递如果传递的是对象的话,传递过程中需要复制对象,因此会调用复制构造函数,该函数的作用就是创建某个对象的临时副本。而当函数返回时,传递该对象时创建该对象的副本会被删除,这是会自动调用析构函数,而如果返回的仍然是对象的话,并且仍采用按值传递的方式,那么又会调用复制构造函数建立一个对象的临时复本,当该值成功返回给被调用函数时,在调用该对象的析构函数删除临时拷贝并释放内存。
例如:
#include <iostream>using namespace std;class A{public:A(){cout<<"执行构造函数创建一个对象\n";}A(A&){cout<<"执行复制构造函数创建该对象的副本\n";}~A(){cout<<"执行析构函数删除该对象\n";}};A func(A one){return one;}int main(){A a;func(a);return 0;}
按址传递:
#include <iostream>using namespace std;void swap(int *a,int *b){int c;cout<<"swap函数中,交换前,a:"<<*a<<"b:"<<*b<<endl;c=*a;*a=*b;*b=c;cout<<"swap函数中,交换后,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;}
解决了按值传递的开销大的缺点,在传递对象时:
#include <iostream>using namespace std;class A{public:A(){cout<<"执行构造函数创建一个对象\n";}A(A&){cout<<"执行复制构造函数创建该对象的副本\n";}~A(){cout<<"执行析构函数删除该对象\n";}};A func(A *one){return
* one;}int main(){A a;func(&a);return 0;}
减少了一次复制构造和析构:
#include <iostream>using namespace std;class A{public:A(){cout<<"执行构造函数创建一个对象\n";}A(A&){cout<<"执行复制构造函数创建该对象的副本\n";}~A(){cout<<"执行析构函数删除该对象\n";}};A * func(A *one){return one;}int main(){A a;func(&a);return 0;}
减少了两次复制构造和析构:
注:
按址传递对象虽然可以避免重复调用复制构造函数和析构函数,但是由于它得到了该对象的内存地址,可以随时修改对象的数据,所以它实际上破坏了按值传递的保护机制。用const指针来接收对象可以防止任何试图对该对象所进行的操作行为,并且保证返回一个不被修改的对象。
例如:
#include <iostream>using namespace std;class A{public:A(){cout<<"执行构造函数创建一个对象\n";}A(A&){cout<<"执行复制构造函数创建该对象的副本\n";}~A(){cout<<"执行析构函数删除该对象\n";}void set(int i){x=i;}int get()const{return x;}private:int x;};const A*const func(const A *const one){one->get();//one->set(99);//one++;return one;}int main(){A a;a.set(11);const A*const p=func(&a);//p++;//p->set(11);cout<<p->get()<<endl;return 0;}
按别名传递:
因为定义引用的别名就是常量,所以不存在更改其的说法。故可以更好的取代按址传递对象。#include <iostream>using namespace std;void swap(int &a,int &b){int c;cout<<"swap函数中,交换前,a:"<<a<<"b:"<<b<<endl;c=a;a=b;b=c;cout<<"swap函数中,交换后,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;}
例如:
#include <iostream>
using namespace std;
class A
{
public:
A(){cout<<"执行构造函数创建一个对象\n";}
A(A&){cout<<"执行复制构造函数创建该对象的副本\n";}
~A(){cout<<"执行析构函数删除该对象\n";}
void set(int i){x=i;}
int get()const{return x;}
private:
int x;
};
const A& func(const A & one)
{
//one.set(33);
return one;
}
int main()
{
A a;
a.set(11);
A const &b=func(a);
cout<<b.get()<<endl;
//b.set(33);
cout<<b.get()<<endl;
return 0;
}
using namespace std;
class A
{
public:
A(){cout<<"执行构造函数创建一个对象\n";}
A(A&){cout<<"执行复制构造函数创建该对象的副本\n";}
~A(){cout<<"执行析构函数删除该对象\n";}
void set(int i){x=i;}
int get()const{return x;}
private:
int x;
};
const A& func(const A & one)
{
//one.set(33);
return one;
}
int main()
{
A a;
a.set(11);
A const &b=func(a);
cout<<b.get()<<endl;
//b.set(33);
cout<<b.get()<<endl;
return 0;
}