# 函数
自动对象
指针形参
指针的行为和其他非引用类型一样。当执行指针拷贝操作时,拷贝的是指针的值。拷贝前后的指针是两个不同的指针,如
1 | void reset(int *ip) |
C++中尽量用引用类型代替指针。
使用引用避免拷贝
大的容器和类拷贝一次很亏,或者有的容器根本不支持拷贝操作。所以应该将这种形参定义成引用形式。如果不需要改变对象的值,就应该把它定义成常量引用。如
1 | bool isShorter(const string &s1, const string &s2) |
注:尽量使用常量引用。普通引用除了会给人一种变量可修改的感觉之外,还会限制函数传入参数的类型,例如 string &s 不允许字面值作为实参来初始化它,而 const string &s 则允许。
使用引用形参返回额外信息
如果想让一个函数返回不止一个值,除了使用 pair 或其他数据类型之外,还可以给函数传入一个额外的引用形参,如
1 | string::size_type find_(const string &s1, char c, string::size_type &occurs) //函数功能是判断s1中是否存在c,并返回c所在的索引和c出现的次数。其中&occurs就作为引用形参来返回“出现次数”这个额外信息。也就是说在调用这个函数时要传入一个变量来存储这个occurs |
调用时直接 auto index = find_(s, ‘o’, ctr); 就可以在更新ctr的值的同时返回index。
const 中的形参和实参
当使用实参初始化形参时,会忽略掉顶层 const 。所以如下两个函数不算重载,他们是一样的
1 | void fcn(const int i) {} |
所以这样是合法的
1 | void fun(int i) |
数组形参
由于数组不能拷贝的特性,所以不能使用传值的方法来传入数组参数,但是可以把形参写成类似数组的形式:
1 | void print(const int*); |
这三个函数是等价的,形参都是 const int* 当传入数组名字时就自动变成了指向第一个元素的指针了。
也可以将数组引用作为参数,如
1 | void print(int (&arr)[10]) //这样虽然限制了数组的维度,但是也限制了函数只能处理10个元素的数组,多一个少一个都不行 |
main: 处理命令行选项
1 | int main(int argc, char *argv[]) {} //char *argv[]也可以定义成char **argv。两个等价 |
argv: 是一个数组,里面是 C-style 字符串指针,第一个元素 argv[0] 存的是程序的名字,它不是由用户输入的。从 argv[1] 开始才是用户输入的参数。最后一个指针之后的元素值保证为0
argc: 表示数组中字符串的数量
例如
1 | 命令prog -d -o ofile data0中 |
含有可变形参的函数
如果函数的实参数量未知但是类型相同,可以使用 initializer_list 类型的形参。该标准库定义在 <initializer_list> 中,其提供的操作如下
1 | initializer_list<T> lst; //默认初始化T类型元素的空列表 |
注:initializer_list 对象中的元素永远是常量值,其中元素的值不能改变!
用法如下:
1 | void error_msg(initializer_list<string> il) |
引用返回左值
调用一个返回引用的函数得到左值,其他返回类型得到右值。用法如
1 | char &get_val(string &str, string::size_type ix)//返回的是非临时变量的引用,是合法的 |
列表初始化返回值
C++11标准规定函数可以返回花括号包围的值的列表,如
1 | vector<string> process() |
如果函数返回值是内置类型,那么花括号里最多一个值。
main返回值
1 | int main() |
main函数不能调用自己,所以没有递归
声明一个返回数组指针的函数
声明这种函数一定要带上数组维度。格式如下
Type (*function(parameter_list)) [dimension]
Type代表元素类型,dimension表示数组大小。例如
1 | int (*func(int i)) [10]; |
C++11中简化了这种声明方式,可以采用尾置返回类型。它跟在参数列表后面并以 -> 开头,紧接着是返回值类型。最后在前面放一个auto,例如
1 | auto func(int i) -> int(*)[10]; |
可以轻易看出,func接受一个int实参,返回值是一个指向10个int元素数组的指针。
如果知道函数返回的指针将指向哪个数组,也可以使用decltype关键字。由于decltype不负责把数组类型转化成指针,所以decltype的结果是数组,还需要在函数声明中加一个*才表示返回函数指针。如下
1 | int odd[] = {1, 3, 5, 7, 9}; |
函数重载
函数重载指的是函数名字相同但是形参列表不同。main函数不能重载
不允许两个函数除了返回类型外其他所有类型都相同!
const_cast 和重载
1 | const string &shorterString(const string &s1, const string &s2) |
默认实参
一旦某个形参被赋予了默认值,它后面的所有形参都必须有默认值。
局部变量不能作为默认实参。
constexpr函数
使用constexpr函数必须遵守以下约定:函数的返回值类型以及所有形参的类型都得是字面值类型(算数类型、引用和指针类型);函数体中必须有且只有一条return语句。constexpr函数体内也可以包含其他语句,只要这些语句在运行时不执行任何操作(如空语句、类型别名、using声明等)即可。如
1 | constexpr int new_sz() { return 42; } |
constexpr函数被隐式的指定为内联函数。也允许constexpr函数的返回值不是常量,也就是说constexpr函数不一定返回常量表达式。
通常将内联函数和constexpr函数定义在头文件中。
函数指针
函数指针指向某种函数类型,函数类型由它的返回类型和形参类型共同决定。如
1 | bool lengthCompare(const string&, const string&); |
在重载函数中使用函数指针,指针的类型必须和重载函数中的某一个精确匹配。
函数指针当做形参
函数本身不能当形参,但是函数指针可以。如
1 | void useBigger(const string &s1, const string &s2, bool pf(const string &, cosnt string &));//第三个参数会自动转换成函数指针。 |
返回指向函数的指针
方法有多种,如
1 | using F = int(int*, int); //F是函数类型的类型别名 |