绑定完请刷新页面
取消
刷新

分享好友

×
取消 复制
C++:为何不建议用string作为函数参数
2019-12-11 10:37:53

在C++中,函数形参一般带“&”和“*”,例如:int*、char*、int&等。这是为什么呢?此处以常用的string字符串数据类型来引入今天要讲的话题;

在C语言中是没有字符串这个数据类型的,C++在C语言的基础上升级了string类。如果很多从CSharp、Java等语言转C++的开发人员在用字符串做函数参数的时候都会很自然的用string类型,但是作为正宗的C++开发者都会建议你不要用string类型作为函数参数。

CSharp的数据类型

CSharp里面把数据类型分为两大类,值类型和引用类型;值类型包括基本数据类型(int ,double等),结构和枚举;引用类型包括接口,数组,Object类型,类,委托,字符串,null类型等;一般来说,结构体类型的数据就是值类型,而类构造的数据就是引用类型;

引用类型和值类型都继承自System.Object类。不同的是,几乎所有的引用类型都直接从System.Object继承,而值类型则继承其子类,即直接继承System.ValueType。

请看下面这段代码:

public class People
 {
 public string Name { get; set; }
 public int Age { get; set; }
 public void reset(string name, int age)
 {
 name = "xiaoli";
 age = 200;
 }
 public void reset(People p)
 {
 p.Name = "xiaoli";
 p.Age = 200;
 }
 }
 class Program
 {
 static void Main(string[] args)
 {
 string name = "xiaoming";
 int age = 100;
 People p = new People();
 p.Name = name;
 p.Age = age;
 People p2 = new People();
 p2.Name = "xiaohua";
 p2.Age = 90;
 Console.WriteLine(p.Name);
 Console.WriteLine(p.Age);
 p.reset(name,age);
 Console.WriteLine(name);
 Console.WriteLine(age);
 p.reset(p);
 Console.WriteLine(p.Name);
 Console.WriteLine(p.Age);
 Console.Read();
 }


运行结果为:

xiaoming
100
xiaoming
100
xiaoli
200


int类型为值类型,形参是不能改变实参的值的,但是People是引用类型,形参可以改变实参的值。引用类型指向的其实是一个内存地址,string 虽然是引用类型 不过是不可变的。

但是CSharp提供ref关键字来使得值类型的实参在传递时也可以被修改

public class People
 {
 public string Name { get; set; }
 public int Age { get; set; }
 public void reset(ref string name, ref int age)
 {
 name = "xiaoli";
 age = 200;
 }
 public void reset(ref People p)
 {
 p.Name = "xiaoli";
 p.Age = 200;
 }
 }
 class Program
 {
 static void Main(string[] args)
 {
 string name = "xiaoming";
 int age = 100;
 People p = new People();
 p.Name = name;
 p.Age = age;
 People p2 = new People();
 p2.Name = "xiaohua";
 p2.Age = 90;
 Console.WriteLine(p.Name);
 Console.WriteLine(p.Age);
 p.reset(ref name,ref age);
 Console.WriteLine(name);
 Console.WriteLine(age);
 p.reset(ref p);
 Console.WriteLine(p.Name);
 Console.WriteLine(p.Age);
 Console.Read();
 }
 }


为何C++函数参数都带*或者&

前面通过CSharp讲到数据分值类型和引用类型,在C++中,引用类型是要显示定义的。

int n;
int& r = n;


如果在函数参数传递时,形参不声明为引用类型,参数传递方式是传值的。传引用的方式要求函数的形参是引用。

那究竟形参带&与不带有什么区别呢?

void func(int& i){i = 110 ;}
void func2(int i){i = 110 ;}
void main()
{
	int n = 200 ;
	int n2 = 300 ;
	func(n);
	func2(n2);
	cout<<n<<endl;
	cout<<n2<<endl;
	system("pause");
}

首先看运行结果:

110
300


再看汇编的区别:

func(n);
00C4443C lea eax,[n] 
00C4443F push eax 
00C44440 call func (0C41271h) 
00C44445 add esp,4 
	func2(n2);
00C44448 mov eax,dword ptr [n2] 
00C4444B push eax 
00C4444C call func2 (0C4143Dh) 
00C44451 add esp,4


先说一下几个指令:

lea:load effective address, 加载有效地址,可以将有效地址传送到指定的的寄存器;

mov:在CPU内或CPU和存储器之间传送字或字节

push:入栈

从上面运行结果和汇编代码来看,当采用引用类型作为形参时,它将变为实参列表中相应变量的别名,对形参进行的任何更改都将真正更改正在调用它的函数中的变量。引用变量本身并不需要分配内存,而是直接对被引用对象取的别名;

引用变量示意

当采用值传递方式时:当函数被调用时,在“栈”中就会分配出一块新的存储空间,用来存放形参和函数中定义的变量(局部变量)。实参的值会被复制到栈中存放对应形参的地方,所以形参的值才等于实参。函数执行过程中对形参的修改,其实只是修改了实参的一个拷贝,因此不会影响实参。


好处

从汇编代码可以看出,普通值传递的传参方式,是需要复制实参的,然后将实参的赋本传递给形参。而引用传参方式直接取实参的有效地址,所以在运行效率上引用传参速度更快更节省内存开销。实参对象所占内存越大,开销越大,对效率的影响也越大;

指针传递(地址调用)

还有一种方式也能避免普通值传参方式的大开销和低效率问题,那就是直接将对象的地址传递给形参。

void func(int& i){i = 110 ;}
void func2(int i){i = 110 ;}
void func3(int* i){*i = 110 ;}
void main()
{
	int n = 200 ;
	int n2 = 300 ;
	int n3 = 400 ;
	int* n4 = &n3 ; 
	func(n);
	func2(n2);
	func3(&n3);
	func3(n4);
	cout<<n<<endl;
	cout<<n2<<endl;
	cout<<n3<<endl;
	system("pause");
}


查看汇编代码:

func(n);
00327009 lea eax,[n] 
0032700C push eax 
0032700D call func (0321271h) 
00327012 add esp,4 
	func2(n2);
00327015 mov eax,dword ptr [n2] 
00327018 push eax 
00327019 call func2 (032143Dh) 
0032701E add esp,4 
	func3(&n3);
00327021 lea eax,[n3] 
00327024 push eax 
00327025 call func3 (0321442h) 
0032702A add esp,4 
	func3(n4);
0032702D mov eax,dword ptr [n4] 
00327030 push eax 
00327031 call func3 (0321442h) 
00327036 add esp,4


从上述汇编代码可以看出func3(n4);本质上是值传递,它所传递的是一个地址值,不管其所指向的对象如何,其在函数调用时复制的只是一个四字节的地址而已,对内存开销和效率影响不大。而func3(&n3);却又是引用传参方式。


如何避免实参被修改

有没有什么办法既保证效率又保证实参的安全性?既然不想实参在函数调用过程中被修改,那么C++有一个关键字--const可以达到目的。

void func(const int& i){i = 110 ;}
void func3(const int* i){*i = 110 ;}



分享好友

分享这个小栈给你的朋友们,一起进步吧。

IT知识联盟
创建时间:2019-07-05 15:30:45
分享收集到的大小知识点
展开
订阅须知

• 所有用户可根据关注领域订阅专区或所有专区

• 付费订阅:虚拟交易,一经交易不退款;若特殊情况,可3日内客服咨询

• 专区发布评论属默认订阅所评论专区(除付费小栈外)

栈主、嘉宾

查看更多
  • 王超
    栈主

小栈成员

查看更多
  • ?
  • youou
  • gamebus
  • chinacc
戳我,来吐槽~