- 当形参是引用类型时, 它对应的实参被引用传递, 或者函数被传引用调用. 引用形参是它绑定的对象的别名, 也就是说, 引用形参是它对应的实参的别名
- 当实参的值被拷贝给形参时, 形参和实参时两个相互独立的对象. 我们说这样的实参被值传递, 或者函数被传值引用.
传值参数
- 此时函数对形参做的所有操作都不会影响实参.
指针形参
void reset(int *ip){
*ip = 0; // 改变指针ip所指对象的值.
ip = 0; // 只改变了ip的局部拷贝, 实参未被改变.
}
int main() {
int i = 42;
reset(&i); // 改变 i 的值而非 i 的地址
cout << "i = " << i << endl;
}
在 c++ 语言中, 建议使用引用类型的形参代替指针
传引用参数
对于引用的操作实际上是作用在引用所引的对象上.
void reset(int &i){ // i 是传给 reset 函数的对象的另一个名字
i = 0; // 改变了 i 所引对象的值
}
调用这一版本的 reset
函数时, 我们直接传入对象, 无需传入对象的地址.
int main() {
int j = 42;
reset(j); // 改变 i 的值而非 i 的地址
cout << "j = " << j << endl; //输出 j = 0
}
使用引用避免拷贝
- 拷贝大的类类型对象或者容器对象比较低效, 甚至有的类(包括IO类)不支持拷贝操作. 当某种类型不支持拷贝操作时, 函数只能通过引用形参访问该类型的对象.
使用引用形参返回额外信息
- 一个函数只能返回一个值, 而有时函数需要同时返回多个值, 引用形参为我们一次返回多个结果提供了有效的途径.
比如, 我们定义一个名为 find_char
的函数, 它返回在 string
对象中指定某个字符第一次出现的位置. 同时, 我们也希望函数能返回该字符出现的总次数.
有两种方法:
一是定义一个新的数据类型, 让它包含位置和数量两个成员
二更简单: 可以给函数传入一个额外的引用实参, 令其保存字符出现的次数. 例子如下
#include<string>
#include<iostream>
using std::string;
using namespace std;
// 返回 s 中 c 第一次出现的位置索引
// 引用形参 occurs : 负责统计 c 出现的总次数
// 已默认此单词中含有 o
string::size_type find_char(const string& s, char c,
string::size_type& occurs) {
auto ret = s.size(); // 第一次出现的位置(如果有的话)
occurs = 0; // 设置表现出现次数的形参的值
for (decltype(ret) i = 0; i != s.size(); ++i) { //遍历字符串 s, 找到 c 第一次出现的位置
if (s[i] == c) {
if (ret == s.size()) { // 确保只有第一次循环才会执行此 if 中的语句
ret = i;
}
++occurs;
}
}
return ret; // ret 用于返回o出现的位置, 出现次数已通过 occurs 隐式地返回
}
当我们调用 find_char
函数时, 必须传入三个实参:
- 作为查找范围的一个
string
对象 - 要找的字符
- 用于保存字符出现次数的
size_type
(3.2.2节 p79页 记得看看这里)
int main() {
string s = "sht";
string::size_type ctr;
auto index = find_char(s, 'o', ctr);
cout << s << "中 o 出现的次数为" << ctr << ".第一个 o 的位置为" << index << endl;
}
ctr
表示 o 出现的次数index
指向 o 第一次出现的位置
如果 string
对象中没有 o, 则 index
等于 s.size()
而 ctr
等于 0 .
const 形参和实参
当形参有顶层 const
时, 传给它常量对象或者非常量对象都是可以的:
void fcn(const int i){ /*fcn 能够读取 i, 但是不能向 i 写值*/ }
区别下方代码
void fcn(int i){ /*...*/ } // 错误: 重复定义了 fcn(int)