6.1 函数基础
-
调用运算符
( ),作用于一个表达式(函数,或者函数指针),实参初始化形参的顺序不确定
-
自动对象
- 当函数控制路径经过变量定义语句时,创建该对象
- 当到达定义所在块的末尾时,销毁该对象
-
局部静态变量
- 第一次经过定义语句时创建
- 直到程序终止销毁
-
分离式编译
//程序分为多个文件,分别独立编译 //假设有 main.cpp / func.cpp / func.h 三个文件 $ gcc -c main.cpp $ gcc -c func.cpp $ gcc main.o func.o -o main
-
参数传递
- 引用传递:形参为实参的别名
- 值传递:形参和实参分别独立,不能通过形参修改实参
- 使用引用避免拷贝:当无需修改形参时,建议使用常量引用
6.2 参数传递
-
const
形参与实参-
当用实参初始化形参时,会忽略形参的顶层const
-
当形参为顶层const时,传入常量对象或非常量对象都是可行的
void func(const int i){}; void func(int i){}; //二者等价,若同时出现则会报错,重定义
-
-
非常量形参引用带来的问题
-
函数可能会修改实参的值
-
不能使用const对象、字面值传递给普通的引用形参
void func(int &i){}; //调用 func(5); //错误 const int a=10; func(a); //错误,不能把普通引用捆绑到const对象上
-
-
数组形参
-
数组不允许拷贝(所以不能以值传递数组)
-
使用数组时,通常会将其转为指针
void p(const int*); //下面两者等价于前面 void p(const int[]); void p(const int[10]); //10并没有限制作用,只是一个期望值
-
数组引用&数组指针形参
void p(int (&arr)[10]); //10是类型的一部分 //传递多为数组,下面二者等价 void p(int (*arr)[10]); void p(int arr[][10]);
-
-
可变形参
- 如果所有的实参类型相同,则可以传递一个名为initializer_list的标准库类型
- 类型不同,使用可变参数模板(此处没有细讲)
- 使用省略符形参
//initializer_list //传递值得序列,将其放入{}内 initializer_list lst2(lst); //不拷贝lst中得元素,共享内容 //initializer_list的元素永远是常量,不能改变元素的值 //省略符形参,只能出现在最后 void func(parmater-list,...); //省略部分无需类型检查
6.3 返回值
-
有返回值函数
-
返回值用于初始化调用点的一个临时量,该临时量为函数调用结果
-
不要返回局部变量的引用和指针
-
列表初始化返回值
vector<string> p() { return {"fun","okey"}; }
-
main 函数的返回值,可以使用机器无关的
EXIT_FAILURE EXIT_SUCCESS
-
-
返回数组指针
-
采用类型别名
using arrT=int[10]; arrT* func(int i);
-
返回数组指针
//如果想定义一个返回数组指针的函数,数组的维度必须在函数名后面 //格式 Type (*func(paramter-list))[dimension]; //中间的圆括号必须存在,否则返回类型就变成了指针数组,不合法 int (*func(int i))[10]; //由内向外解释 func(int i); //调用该函数需要一个int类型参数 (*func(int i)); //意味着函数调用结果可以解引用 (*func(int i))[10]; //解引用结果得到一个大小为10的数组 int (*func(int i))[10]; //其类型为int
-
尾置返回类型
auto func(int i)->int(*)[10];
-
使用
decltype
int odd[]={1,3,5,7,9}; decltype(odd) *arrPtr(int i); //注意加 *
-
6.4 函数重载
-
重载与
const
形参//顶层const形参不影响传入参数的对象 Record lookup(Phone); //等价于 Record lookup(const Phone);
-
底层
const
形参保留Record lookup(Account&); Record lookup(const Account&); //与前者不同,重载
-
默认实参
//默认实参一般放在参数列表的后面 string screen(string words,int width=100,int height=300); //函数的后续声明只能为之前那些没有默认值的形参添加默认实参 string screen(sz,sz,char=' '); string screen(sz,sz,char='*'); //错误,重复声明 string screen(sz=24,sz=80,char); //正确重载,添加默认参数,注意之前的默认参数无需再写出来
-
函数匹配
- 找到函数名相同的候选函数
- 找到能使用指定实参调用的可行函数(形参数量与实参类型相同或者能够转换)
- 最佳匹配
- 该函数的每个实参的匹配都不劣于其他可行函数需要的匹配
- 至少有一个实参的匹配由于其他可行函数提供的匹配
- 若存在二义性,则会报错
6.5 调试与运行的一些操作
-
内联
通过加上
inline
关键字,再调试时,函数会自动展开,适用于规模小、流程直接、使用频繁的函数 -
assert & NDEBUG
assert (expr); //expr为假,输出信息并终止程序 //expr为真,什么都不做 //assert的行为依赖于NDEBUG的状态 - 定义了NDEBUG,什么也不做 - 未定义NDEBUG,执行运行时检查
-
用NDEBUG编写条件调试代码
void print(int i) { #ifndef NDEBUG //输出调试信息 #endif }
-
预处理器用于调试的名字
__func__ //存放当前调试函数的名字 __FILE__ //存放当前文件名 __LINE__ //当前行号 __TIME__ //文件编译时间 __DATE__ //文件编译日期
6.6 函数指针
-
函数的类型
//由返回类型和形参类型共同决定 //若要声明函数指针,只需用指针替换函数名 bool lengthCompare(const string&,const string&); //函数指针 bool (*pf)(const string&,const string&); pf=lengthCompare; pf=&lengthCompare; //二者都可行 //可以直接使用函数指针调用函数,并且无需提前解引用 bool b1=pf("aaa","bbb"); bool b2=(*pf)("aaa","bbb"); //先解引用,再调用
-
函数指针作形参
//是函数类型,但是会自动转为指针 void useBigger(string a,string b,bool pf(const string &,const string&)); //直接使用指针类型 void useBigger(string a,string b,bool (*pf)(const string &,const string&)); //使用时,可以直接使用函数作为实参,会自动转为指针 useBigger(A,B,lengthCompare);
-
返回函数指针
-
使用别名
using F=int(int*,int); //F是函数类型 using PF=int(int*,int); //PF:函数指针 //返回类型不会自动将函数类型转为指针,必须显式指定
-
直接声明
int (*fun(int))(int*,int); //由内向外解释 //fun有参数列表,所以fun是一个函数,前面有*,则其返回的是一个指针,指针本身也包含参数列表,故其指向一个函数,该函数的返回类型是int
-
使用尾置类型
auto fun(int)->int(*)(int*,int);
-