第六章 函数
C++11
- initialize_list -- 6.2.6
6.1 函数基础
- 函数调用的两项工作
- 控制权:主调函数中断,被调函数执行
- 隐式定义并初始化形参
- return的两项工作
- 返回值
- 控制权: 转移到主调函数
- 实参的数目和类型与形参对应,如果不对应需要能够隐式转化
- 形参列表不能忽略,如果没有形参可以为空或者void
- 每个形参必须带有自己的类型
- 形参不能重名,也不能与函数最外层的局部变量重名
- 返回类型不能是数组或者函数,可以是其指针
6.1.1 局部对象
名字有作用域,对象有生命周期
- 自动对象:控制路径经过变量定义语句时创建对象,到达所在块的末尾时销毁
- 局部静态对象:控制路径经过变量定义语句时创建对象,程序结束时销毁
6.1.2 函数声明
- 函数声明/函数原型:不需要写函数体,使用分号代替
- 应该在头文件中声明,源文件中定义,然后原文件包含头文件,其他使用函数的原文件或头文件也只包含头文件,这样修改函数接口时只需要修改一处声明。
6.1.3 分离式编译
- 对于将函数定义在其他源文件中的情况,分离式编译使得可以只重新编译修改过的源文件,生成对象代码.obj或.o,然后链接在一起
CC -c factmain.cc # factmain.o
CC -c fact.cc # fact.o
CC factmain.o fact.o #factmain.exe
CC factmain.o fact.o -o main #main.exe
6.2 参数传递
- 引用传递:形参是实参的别名
- 值传递:形参与实参是两个对象
6.2.1 传值参数
- 改动不影响实参
- 指针形参:指针形参不影响实参,但是可以用于修改所指对象;建议用引用传递,而非指针
6.2.2 传引用参数
- 可以避免使用拷贝,对于大的类对象或者容器对象比较合适;此外有些类类型不能拷贝,如IO
- 可以使用引用传递返回值
6.2.3 const形参和实参
- 如果const是顶层const,实参是常量或者非常量都可以,因此此时不能用const形参与非const形参做区分,实际是相同的参数
- 指针或者引用的形参与const:
- 对于底层const的指针或者引用,可以使用常量或者非常量做实参,引用也可以使用字面值做实参
- 对于非底层const的指针或者引用,只能使用非常量,也不能使用字面值
- 如果不修改参数值尽量使用常量引用,因为一般引用的实参选择范围更小,会在传递参数时出现不匹配,此外在不同函数之间重复调用时会出现参数不匹配
6.2.4 数组形参
- 数组不允许拷贝,所以不能进行对数组的值传递,需要通过指针或者引用;
- 指针的类型是数组的元素类型,而引用的类型是数组本身的类型
(1) 指针
- 形参有三种等效的书写方式,其实质上都会被编译器视作
const int*
,此外,对数组大小的说明只有一个提醒作用,不具备实际效果;同样的,尽量使用const。
void print(const int*)
void print(const int[])
void print(const int[10])
(2) 数组大小
- 通过指针传递不能获得数组大小的信息,有三种方法
- 方法一:标记
该方法适合于数组末尾有特殊标记的数组,比如(const) char*的末尾是空字符。
void print(const char* p)
{
if(p)//判单空指针
while(*p)//判断空字符
std::cout<<*p++;
}
- 方法二:迭代器
该方法通过传入begin以及end指针来实现
void print(const int* begin, const int* end)
{
while(begin!=end)
cout<<*begin++;
}
- 方法三:尺寸形参
该方法直接传入数组大小
(3) 应用传递
- 引用传递的数组大小要求只能使用对应大小的数组作为实参,因此限制了自由。
void print(int(&arr)[10])
{
for(auto item:arr)
cout<<item;
}
(4)传递多维数组
void print(int (*arr)[10], int rowsize)
void print(int arr[][10], int rowsize)
6.2.5 main:处理命令行选项
- 第一个参数是字符串数量,第二个参数是字符串数组
- 字符串数组的第一个值是程序名或者是空字符串,最后一个是0,其他是输入参数
int main(int argc, char *argv[])
int main(int argc, char **argv)
6.2.6 含有可变形参的函数
- 有时函数不确定传入的实参个数
- 如果实参个数不确定但是类型相同,可以使用initialize_list,如果类型不相同可以使用可变参数模板
- 此外,c有省略符
(1) initialize_list
- 标准库类型,定义在initialize_list.h中
initialize_list<T> li;
initialize_list<T> li{a,b,c,...};
l2(l1);
l2=l1;
l.size();
l.begin();//指针
l.end();//指针
- initialize_list与vector类似,也是容器模板,但是其内容不可变
- 对应的实参采用花括号传递
string b="bbb",c="ccc";
errmog({"aaa",b,c});
(2)省略符
- 只能够使用c与c++的通用类型,大多数类类型的对象不可用
- 省略符的类型不做检查
- 逗号可选
void foo(...);
void foo(list,...);//逗号可以省略