每次调用函数时都会重新创建它的形参
Note:形参初始化的机理和变量初始化一样
当形参是引用类型时,它对应的实参被引用传递(passed by reference),引用就是对应实参的别名
当实参的值被拷贝给形参时,形参和实参时两个相互独立的对象。我们说这样的实参被值传递(passed by value)
6.2.1传值参数
指针形参
void reset(int *ip)
{
*ip = 0;//改变指针ip所指对象的值
ip = 0; //只改变了ip的局部拷贝,实参未被该改变
}
int main()
{
int i = 42;
reset(&i); //改变i的值而非地址
cout << "i = " << i <<endl;
return 0;
}
熟悉C的程序员常常使用指针类型的形参访问函数外部的对象。在C++语言中,建议使用引用类型的形参代替指针
6.2.2 传引用参数
void reset(int &i)
{
i=0;
}
int main()
{
int j =42;
reset(j);
cout << "j = " << j <<endl;
return 0;
}
使用引用避免拷贝
当某种类型不支持拷贝操作时,函数只能通过引用形参访问该类型的对象
bool isShorter(const string & s1 , const string & s2)
{
return s1.size()<s2.size();
}
int main()
{
const string s1= "a srting ";
const string s2= "one string";
//比较两个string对象的长度
return 0;
}
如果函数无须该百年引用形参的值,最好将其声明为常量引用
使用引用形参返回额外信息
如何定义函数使得它能够既返回位置也返回出现次数?
一种方法是定义一个新的数据类型,让它包含位置和数量两个成员
另一种更简单的方法:可以给函数传入一个额外的引用实参,令其保存字符出现的次数:
6.2.3 const形参和实参
形参的顶层const会被忽略
当形参有顶层const时,传给它常量对象或者非常量对象都是可以的,形参的顶层const会被忽略
void fcn(const int I )
传入
void fcn(const int I )//fcn能读取i但是不能向i写值等同于 void fcn(int i)
指针或引用形参与const
可以用非常量初始化一个底层const对象,但是反过来不行
同时一个普通的引用必须用同类型的对象初始化
int main()
{
int i = 42;
const int *cp = &i; //正确:但是cp不能改变i
const int &r = i; //正确:但是r不能改变i
const int &r2 = 42; //正确
int *p = cp; //错误:p和cp类型不一样
int &r3 = r; //错误:r3和r的类型不匹配
int &r4 = 42; //错误:不能用字面值初始化一个非常量引用
return 0;
}
同样的规则应用到参数传递上
尽量使用常量引用
定义成普通引会给调用者一种误导,即函数可以修改它的实参的值
此外也会限制函数所能接受的实参类型
6.2.4 数组形参
数组两大性质:不允许拷贝;会转换成指针
因为不能拷贝,所以无法以直传递的方式使用数组参数
因为数组会被转换成指针,所以当我们为函数传递一个数组时,实际上传递的是指向数组首元素的指针
//尽管形式不同,但这三个print函数是等价的
//每个函数都有一个const int*的形参
void print(const *int)
void print(const int[]) //可以看出来,函数的意图是作用于一个数组
void print(const int[10]) //这里的维度表示期望数组含有多少个元素,实际不一定
int i = 0 ,j[2] = {0,1};
print(&i);//正确:&i的类型是int*
print(j);//正确:j转换成int*并指向j[0]
Warning:和其他使用数组的代码一样,以数组作为形参的函数也必须保证使用数组时不越界
使用标记指定数组的长度
管理数组实参的第一种方法是要求数组本身包含一个结束标记 (如空字符)
void print(const char *cp)
{
if(cp) //若cp不是一个空指针
while(*cp) //只要指针所指的字符不是空字符
cout << *cp++;//输出当前字符并将指针向前移动一个位置
}
使用标准库规范
传入数组首元素和尾后元素的指针
void print(const int *beg, const int *end)
{
while(beg != end)
cout << *beg++ << endl;
}
显示传递一个表示数组大小的形参
第三种方法,专门定义一个表示数组大小的形参
void print (const int ia[], size_t size)
{
for(size_t i = 0; i!=size; ++i){
cout << ia[i] <<endl;
}
}
需要通过size的值确定要输出多少个元素
数组形参和const
只有当函数确实要改变元素值得时候,才把形参定义成指向非常量的指针
数组引用形参
Note:
f(int & arr[10]) //错误:将arr声明成了引用的数组
f(int (&arr)[10]) // 正确: arr 是具有10个整数得整数数组得引用
传递多维数组
在处理数组的数组时,首元素本身就是一个数组,指针就是一个指向数组的指针
//matrix指向数组的首元素,该数组的元素是由10个整数构成的数组
void print(int (*martix)[10], int rowSize){/*....*/}
Note:再强调
int *martix[10]; //10个指针构成的数组
int (*martix)[10] // 指向含有10个整数的数组的指针
6.2.5 main:处理命令行选项
main可以传递实参
warning:当使用argv中的实参时,一定要季度可选的实参从argv[1]开始;argv[0]保存程序的名字,而非用户输入
6.2.6 含有可变形参的函数
有时无法预知向函数传递几个参数
如果有的实参类型相同,可以传递一个initialize_list的标准库函数;如果类型不同,可变参数模板(p618)
initializer_list形参
如果函数的实参数量未知但是全部实参的类型都相同,可以使用名为 initializer_list 类型的形参,
表示某种特定类型的值的数组。
定义在同名头文件中
表6.1 initializer_list 提供的操作
initializer_list<T> lst; | 默认初始化;T类型元素的空列表 |
initializer_list<T> lst{a,b,c,d....}; | lst的元素数量和初始值一样多;lst的元素是对应初始值的副本;列表中的元素是const |
lst2(lst) or lst2 = lst | 拷贝不会拷贝列表中的元素;拷贝后,原始列表和副本共享元素 |
lst.size() | |
lst.begin() | |
lst.end() |
和vector一样lst也是一种模板类型,定义initializer_list对象时,必须说明列表中所含元素的类型:
和vector不一样的是,initializer_list对象中的元素永远是常量值,我们无法改变initializer_list对象中元素的值。
void error_msg(initializer_list<string> i1){....}
使用:
error_msg("functionX", expected,actual) //expected和actual是string
也可以包含其他形参
省略符形参
省略符形参是为了便于C++访问特殊C代码
这些代码使用了varargs的C标准库功能。
Warning:省略符形参应该仅仅用于C和C++通用的类型。特别应该注意的是,大多数类型的对象在传递给i省略符形参时都无法正确拷贝。
只能出现在最后如
void foo(parm_list, ...);