形参的类型决定了形参和实参交互的方式。如果形参是引用类型,它将绑定到对应的实参上。否则将拷贝后赋值给形参。
一、传值参数
当初始化一个非引用类型的变量时,初始值将拷贝给变量。此时对变量的改变不会影响到初始值。这也是课本上经常遇到的传参方式。
void reset(int i)
{
i++;
}
指针形参
指针的行为和其他非引用类型一样,当执行指针拷贝工作时,拷贝的是指针的值。拷贝之后,两个指针是不同的指针。因为指针指向对象的地址,所以通过指针可以修改它所指向的对象的值。
void reset(int* ip)
{
*ip = 0;//改变了指针ip所指对象的值
ip = 0;//只改变了ip的局部拷贝
}
在C语言中常常使用指针来访问外部变量。但在C++中,我们可以使用引用类型的形参代替指针。
二、值引用传参
引用形参直接关联到其所绑定的对象,而并非这些对象的副本。定义引用时,必须用与该引用绑定的对象初始化该引用。引用形参完全以相同的方式工作。每次调用函数,引用形参被创建并与相应实参关联。
void swap(int &a,int &b)
{
int c = b;
b = a;
a = c;
}
使用引用形参返回额外的信息
引用形参的另一种用法是向主调函数返回额外的结果。函数只能返回单个值,但有些时候,函数有不止一个的内容需要返回。
int search_(const std::string s1, char s, int& index)
{//找到s1中字符为s的返回最后一次出现的位置和次数。
int res = 0;//次数
for (int i = 0; i < s1.size(); i++)
{
if (s1[i] == s)
{
index = i;
res++;
}
}
return res;
}
这样可以做到返回多个值,index作为引用类型被改变值,函数最后返回一个值代表出现次数。但实际上相当于返回多个值。
使用引用避免拷贝
在向函数传递大型对象时,需要使用引用形参,这是引用形参适用的另一种情况。虽然复制实参对于内置数据类型的对象或者规模较小的类类型对象来说没有什么问题,但是对于大部分的类类型或者大型数组,它的效率(通常)太低了。使用引用形参,函数可以直接访问实参对象,而无须复制它。
bool isShorter(const string &s1, const string &s2)
{
return s1.size() < s2.size();
}
其每一个形参都是 const string 类型的引用。因为形参是引用,所以不复制实参。又因为形参是 const 引用,所以 isShorter 函数不能使用该引用来修改实参。
如果使用引用形参的唯一目的是避免复制实参,则应将形参定义为 const 引用。
左值引用与右值引用
&& 定义一个右值引用。右值引用支持移动语义的实现,可以显著提升应用程序的性能。因为它允许从程序中其他地方无法引用的临时对象转移资源。
void change(int&& a)
{
cout << a << endl;
cout << "右值引用" << endl;
}
void change(const int& a)
{
cout << a << endl;
cout << "左值引用" << endl;
}
int main()
{
int a;
a = 5;
change(a+1);//右值
change(a);
}
三、const实参与形参
在C++中,允许我们定义若干具有相同名字的函数,前提是形参列表要有明显的区别。顶层const被忽略掉了。
void fun(const int i){}
//可以读取i,但无法改变
void fun(int i){}//重复定义,错误
指针或引用形参与const
形参的初始化方式与变量的初始化方式一致。我们可以用非常量来初始化一个底层const对象。同时,一个普通的引用必须用同类型的对象初始化。
错误写法:无法用const常量类型初始化string的引用。
void change(std::string& s){}
int main()
{
change("sss");
}
尽量使用常量引用
把函数不会改变的形参定义为(普通)的引用是一种很常见的错误。这么做会带来一种误导,即函数可以修改它的值。此外,使用引用而非常量引用也会极大限制函数所接受的实参类型。
不良设计: 第一个参数应该为const string类型。
std::string::size_type find_char(std::string& s, char c);
则这种情况下,find_char(“hello”,‘o’);则会报错。