参数传递的三种方式

参数传递的三种方式:
  • 按值传递:在调用函数传递参数时,编译器会自动在栈中创建该参数的拷贝,然后将此拷贝传递给被调用函数,故对被调用函数的参数改变只是改变了拷贝,而无法改变主调函数的参数本身的值。
  • 按址传递:在调用函数传递参数时,传递参数的地址,这样指针寻址之后修改的也就是参数本神的值。
  • 按别名传递:在调用函数传递参数时,传递参数的别名,这样对别名的修改也是对参数本身的修改。
按值传递
#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;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值