函数
3.返回类型和return语句
return;
return expression;
无返回值的return语句用在返回类型是void的函数中。返回void的函数不要求非得有return语句,最后一句会隐式执行return。
强行令void函数返回其他类型的表达式会产生编译错误。
只要函数的返回类型不是void,则函数内的return语句必须返回一个值。
当函数返回局部变量时,意味着将返回值拷贝到调用点,返回一个副本。
不要返回局部对象的引用或指针
函数终止,局部变量的引用将指向不再有效的内存区域。
返回类类型的函数和调用运算符
//shortString返回较短的对象
auto sz = shortString(s1, s2).size();
引用返回左值
函数的返回类型决定函数调用是否是左值。
调用一个返回引用的函数得到左值。直白了说就是:函数调用可以放等号左边用。
char & get_val(string &str, string::size ix)
{
return str[ix];
}
int main()
{
string s("a value");
cout << s << endl; //输出 a value
get_val(s, 0) = "A"; //get_val当左值
cout << s << endl; //输出 A value
return 0;
}
列表初始化返回值,返回值类型是类类型
C++11新标准规定,函数可以返回花括号包围的值的列表。
vector<string> process()
{
if (expected.empty())
return {}; //返回的vector对象为空
else if (expected == actual)
return {"functionX", "okey"}; //返回的vector对象用两个元素初始化
else
return {"functionX", expected, actual}};//返回的vector对象用三个元素初始化
}
主函数main的返回值
main函数可以没有return 语句,将隐式插入return 0;
返回数组指针
数组不能被拷贝,所以函数不能返回数组。可以返回数组的指针或引用。
- 使用别名
typedef int arrT[10]; //arrT的类型是含有10个整数的数组
using arrT = int[10];
arrT* func(int i); //返回值的类型是指向含有10个整数的数组的指针
- 不使用别名
int (*func(int i)) [10];
- 使用尾置类型
任何函数都可以使用尾置返回类型
尾置返回类型跟在形参列表后面并以一个->符号开头
auto func(int i) -> int(*)[10];
- 使用decltype
int odd[] = {1,3,5,7,9};
int even[] = {0,2,4,6,8};
decltype(odd) *arrPtr(int i)
{
return (i % 2) ? &odd : &even; //返回一个指向数组的指针
}
4.函数重载
重载与const形参
顶层const不影响传入函数的对象
int look(int);
int look(const int); //错误,重复声明了int look(int)
如果形参是指针或者引用,可以通过区分指向的常量对象还是非常量对象来实现函数重载。此时const是底层的。
int look(int&);
int look(const int&); //新函数
int look(int *); //新函数
int look(const int *); //新函数
重载与const_cast
……
……
重载确定:编译器首先将调用的实参与重载集合中的每个函数的形参进行比较,根据比较结果决定到底调用哪个函数。
- 找到了最佳匹配,调用该函数。
- 找不到任何一个匹配,编译器发出匹配出错。
- 多于一个函数可以匹配,都不是最佳选择,二义性调用,出错。
重载与作用域
在局部作用域内声明函数,就隐藏了其他重载函数。
void print(const string &);
void print(double);
void foo(int ival)
{
void print(int); //在局部作用域声明,虽然不是好的选择
print("Value: "); //错误, print(const string &);被隐藏了
print(ival); //正确
print(3.14); //正确,调用print(int);,3.14转换为3。
}
5.特殊用途语言特性
- 默认实参
- 内联函数和constexpr函数
- 调试帮助
默认实参
typedef strin::size_type sz;
string screen (sz ht = 24, sz wid = 80, char backgrnd = ' ');
可以为一个或多个形参提供默认值,上例每个形参都提供了默认值。
一旦某个形参被赋予了默认值,它后面的所有形参都必须有默认值。所以,使用默认值的形参放后面。
string window;
window = screen(); //等价于 screen(24, 80, ' ');
window = screen(66); //等价于 screen(66, 80, ' ');
window = screen(66, 256); // screen(66, 256, ' ');
window = screen(66, 256, '#'); // screen(66, 256, '#');
window = screen(, , '?'); //错误,只能省略尾部实参
所以,使用默认值的形参放后面。
给定的作用域内,一个形参只能被赋予一个默认实参,后续的声明只能为之前没有默认值的形参添加默认实参。该形参右侧的所有形参都必须有默认值。
string screen(sz, sz, char = ' ');
string screen(sz, sz, char = '*'); //错误,重复声明。一个形参只能被赋予一个默认实参
string screen(sz, sz = 80, char = '*'); //后续的声明只能为之前没有默认值的形参添加默认实参。该形参右侧的所有形参都必须有默认值。
局部变量不能作为默认实参。
内联函数和constexpr函数
调用函数包含一系列的工作:保存寄存器的值,并在返回时恢复;可能需要拷贝实参,程序转向一个新的位置继续执行。
内联函数可避免函数调用的开销。
inline
内联函数用于规模较小,流程直接,频繁调用的函数。
constexpr函数是指能用于常量表达式的函数。函数的返回值类型和所有的形参类型都是字面值类型,函数体中必须有且仅有一条return语句。
constexpr int new_sz() { return 42; }
内联函数和constexpr函数放在头文件里。
调试帮助
- assert预处理宏
assert宏定义在cassert头文件中。
assert(expr);
//expr为假,assert输出信息并终止程序的执行。
//expr为真,assert什么也不做
- NDEBUG
assert 也依赖于NDEBUG,如果定义了NDEBUG,assert什么也不做。
默认状态下没有定义NDEBUG,此时assert将执行运行时检查。
NDEBUG也可以自己编写条件调试代码。
#ifndef NDEBUG
//调试代码
#endif
如果定义了NDEBUG,上述代码将被忽略。
6.函数匹配
一、确定候选函数
与被调函数同名,其声明在调用点可见。
二、选出可行函数
形参数量与实参数量相同
形参与实参类型相同,或实参类型能转换成形参的类型
三、选出最佳匹配
实参类型与形参类型与接近,匹配的越好。
7.函数指针
函数指针指向的是函数而非对象。
bool lengthCompare(const string &, const string &); //函数声明
//指向一个函数,该函数的参数是两个const string的引用,返回值是bool
bool (*pf)(const string &, const string &); //pf是函数指针
使用函数指针
pf = lengthCompare;
pf = &lengthCompare; //二者等价
bool b1 = pf("hello", "goodbye");
bool b2 = (*pf)("hello", "goodbye");
bool b3 = lengthCompare("hello", "goodbye"); //三者等价
函数指针作为形参
//二者等价
void useBigger(const string &s1, const string &s2, bool pf(const string &, const string &));
void useBigger(const string &s1, const string &s2, bool (*pf)(const string &, const string &));
useBigger(s1, s2, lengthCompare);
函数指针作为返回值
//使用别名
using F = int (int *, int); //F是函数类型,不是指针
using pF = int(*)(int *, int); //pF是指针类型
pF f1(int); //正确,返回值是函数指针类型
F* f2(int); //正确,返回值是函数指针类型
F f3(int); //错误,不能返回一个函数
auto f4(int) -> int (*)(int*, int);