文章目录
函数
我们用调用运算符(())来执行函数。它作用于一个表达式,该表达式是函数或者执行函数的指针。
局部静态对象
可以将局部变量定义成static类型从而获得这样的对象,。**局部静态对象(local static object)**在程序的执行路径第一次经过对象定义语句初始化,并且直到程序终止才会被销毁,在此期间即使对象所在的函数结束执行也不会对它造成影响。
我们建议变量在头文件中声明,在源文件中定义;函数在头文件中声明而在源文件中定义。
当用实参初始化形参时会忽略掉顶层const。
面对类类型对象或者容器对象时,尽量使用引用,避免拷贝。如函数无须修改引用形参的值,就将其定义为const。
在C++中,允许我们定义若干具有相同名字的函数,不过前提是不同函数的形参列表是有明显区别的。
C++允许用字面值常量初始化常量引用。
传递数组引用参数
C++语言允许将变量定义成数组的引用。
void print(int (&arr)[10])
{
for (auto elem : arr)
std::cout << elem << std::endl;
}
传递多维数组
void print(int (*matrix)[10], int rowSize);
用引用去传递多维数组
void print(int (&a)[2][10])
{
for(int i = 0; i < 2; i++)
{
for(int j = 0; j < 10; ++j)
std::cout << a[i][j] << " ";
}
}
含有可变参数的函数
为了编写能处理不同数量实参的函数,C++11新标准提供了两种主要的方法:
- 如果所有的实参类型相同,可以传递一个名为initializer_list的标准库类型。
- 如果实参的类型不同,可以编写一种特殊的函数,也就是所谓的可变参数模板。
C++还有一种特殊的形参类型(即省略符),可以用它传递可变数量的实参。这种功能一般用于与C函数交互的接口程序。
initializer_list
如果函数的实参数量未知但是全部实参的类型都相同,我们可以它。
initializer_list提供的操作
initializer_list list | 默认初始化,T类型元素的空列表 |
---|---|
initializer_list lst{a,b,c,…}; | lst的元素数量和初始值一样多;lst的元素是对应初始值的副本;列表中的元素是const的。 |
lst2(lst) 或 lst2 = lst | 拷贝或赋值一个initializer_list对象,不会拷贝列表中的元素;拷贝后,原始列表和副本共享元素 |
lst.size() | 列表中的元素数量 |
lst.begin() | 返回lst中首元素的指针 |
lst.end() | 返回lst中尾元素下一个位置的指针 |
initializer_list对象中的元素永远是常量值,无法改变对象中元素的值。
如果想向initializer_list形参中传递一个值的序列,则必须把序列放在一对花括号内。
省略符形参
省略符形参只能出现在形参列表的最后一个位置:
void foo(parm_list, ...);
void foo(...);
不要返回局部对象的引用或指针
函数完成后,它所占用的存储空间也随之被释放掉。函数终止意味着局部变量的引用将指向不再有效的内存区域。
调用一个返回引用的函数得到左值,其他返回类型得到右值。
列表初始化返回值
C++11新标准规定,函数可以返回花括号包围的值的列表。此次的列表也用来对表示函数返回的临时量进行初始化。如果列表为空,临时量执行值初始化;否则,返回的值由函数的返回类型决定。
vector<string> process()
{
//expected 和 actual 是string对象
if (expected.empty())
return {}; //返回一个空的vector对象
else if(expected == actual)
return {"functionX", "okay"}; //返回列表初始化的vector对象
}
如果函数返回的是内置类型,则花括号包围的列表最多包含一个值,而且该值所占空间不应该大于目标类型的空间。如果函数返回的是类类型,由类本身定义初始值如何使用。
尾置返回类型
C++11新标准有一种尾置返回类型(trailing return type)的方法。
任何函数的定义都能使用尾置返回,但是这种形式对于返回类型比较复杂的函数最有效。尾置返回类型跟在形参列表后面并以一个->符号开头。为了表示函数真正的返回类型跟在形参列表之后,我们在本应该出现返回类型的地方放置一个auto:
auto func(int i) -> int (*)[10];
使用decltype
如果我们知道函数返回的指针将指向哪个数组,就可以使用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; //返回一个指向数组的指针
}
重载函数
对于重载的函数,它们应该在形参数量或形参类型上有所不同。
不允许两个函数除了返回类型外其他所有的要素都相同。
重载和cosnt形参
顶层const不影响传入函数的对象。一个拥有顶层const的形参无法和另一个没有顶层const形参区分开来。
如果形参是某种类型的指针或引用,则通过区分其执行的是常量对象还是非常量对象可以实现函数重载,此时的const是底层const。
如果我们在内层作用域中声明名字,它将隐藏外层作用域中声明的同名实体。在不同的作用域中无法重载函数名。
默认实参
一旦一个形参被赋予了默认值,则在它后面的所有形参都必须有默认值。
通常应该在函数声明中指定默认实参,并将该声明放在合适的头文件中。
局部变量不能作为默认实参,只要表达式的类型能转换成形参所需的类型,该表达式就能作为默认实参。
用默认实参的名字在函数声明所在的作用域内解析,而这些名字的求值过程发生在函数调用时。
内联函数
内联函数可避免函数调用的开销。将函数指定为内联函数(inline),通常就是将它在每个调用点上“内联地”展开。
在函数的返回类型前面加上关键字inline,这样就可以将它声明成内联函数了。
constexpr函数
**constexpr函数(constexpr function)**是指能用于常量表达式的函数。
函数的返回类型以及所有形参的类型都是字面值类型,而且函数体必须有且只有一条return语句。
constexpr函数体内也可以包含其他语句,只要这些语句在运行时不执行任何操作就行。允许constexpr函数的返回值并非一个常量。
constexpr函数不一定返回常量表达式。
constexpr函数被隐式地指定为内联函数。
内联函数和constexpr函数通常定义在头文件中。
调试帮助
assert 预处理宏
assert(expr);
首先对expr求值,如果表达式为假,assert输出信息并终止程序的执行。如果表达式为真,assert什么也不做。
assert宏常用于检查“不能发生”的条件。
NDEBUG 预处理变量
assert的行为依赖于一个名为NDEBUG的预处理变量的状态。如果定义了NDEBUG,则assert什么也不做。默认状态下,没有定义NDEBUG,此时assert将执行运行时检查。
预处理器对于程序调用的名字:
_ _ func_ _ 输出当前调试的函数的名字
_ _ FILE _ _ 存放当前行号的整型字面值
_ _ LINE _ _ 存放当前行号的整型字面值
_ _ TIME _ _ 存放文件编译时间的字符串字面值
_ _ DATE _ _ 存放文件编译日期的字符串字面值
函数指针
bool (*pf) (const string&, const string&);
当我们把一个函数名作为一个值使用时,该函数自动地转换成指针。
我们可以直接使用指向函数的指针调用该函数,无须提前解引用指针。
decltype返回函数类型时,不会将函数类型自动转换成指针类型。
返回一个指向函数的指针
using PF = int (*)(int, int);
声明一个返回函数指针的函数:
auto fi(int) -> int (*)(int, int); //尾式返回类型方式