函数基础
形参和实参
- 编译器能以任意可行的顺序对实参求值
- 实参数量应与形参数量一致,所以形参一定会被初始化
局部对象
- 形参和函数体内部定义的变量统称为局部变量,局部变量仅在函数的作用域内可见,并将外层作用域中的同名变量隐藏
- 在所有函数体之外定义的对象存在于程序的整个执行过程中。此类对象在程序启动时被创建, 直到程序结束才会销毁
- 局部静态对象在程序的执行路径第一次经过对象定义语句时初始化,并且直到程序终止才被销毁,在此期间即使对象所在的函数结束执行也不会对它有影响:
size_t countCalls() {
static size_t count = 0;
return ++count;
}
- 内置类型的局部静态变量如果没有显式的初始值,则初始化为
0
参数传递
传引用参数
- 使用引用形参避免拷贝:拷贝大的类类型对象或者容器对象比较低效,甚至有的类类型(包括
IO
类型)根本就不支待拷贝操作。当某种类型不支待拷贝操作时, 函数只能通过引用形参访问该类型的对象 - 当函数无须修改引用形参的值时使用常量引用
const
形参和实参
- 和其他初始化过程一样,当用实参初始化形参时会忽略掉顶层
const
,因此下面两个函数是重复定义:
void foo(const int i) {}
void foo(int i) {}
- 尽量使用
const
引用作为形参:如果采用普通引用作为形参,则无法将 const
对象、字面值或需要类型转换的对象传递给函数
默认参数
- 调用含有默认实参的函数时,可以包含该实参,也可以省略该实参
- 一旦某个形参被赋予了默认值,它后面的所有形参都必须有默认值
- 函数调用时实参按其位置解析,默认实参负责填补函数调用缺少的尾部实参
数组形参
- 为函数传递一个数组时,实际上传递的是指向数组首元素的指针
void foo(const int *arr);
void foo(const int arr[]);
void foo(const int arr[10]);
int i = 1;
int arr[5] = {0, 1, 2, 3, 4};
foo(&i);
foo(arr);
- 因为数组是以指针的形式传递给函数的, 所以一开始函数并不知道数组的确切尺寸,调用者应该为此提供一些额外的信息,以确保指针不会越界
- 传递指向数组首元素和尾后元素的指针:
void print(const int *begin, const int *end) {
while (begin != end)
cout << *begin++ << endl;
}
print(begin(arr), end(arr));
main()
处理命令行选项
int main(int argc, char **argv) {}
- 当实参传给
main
函数之后, argv
的第一个元素指向程序的名字或者一个空字符串,接下来的元素依次传递命令行提供的实参。最后一个指针之后的元素值保证为 0
initializer_list
形参
- 如果函数的实参数量未知但是全部实参的类型都相同,可以使用
initializer_list
类型的形参 initializer_list
是一种模板类型,用于表示某种特定类型的值的数组,其提供的常用操作有:
initializer_list<T> lst;
initializer_list<T> lst{a, b, c, ...};
lst2(lst1);
lst2 = lst1;
lst.size();
lst.begin();
lst.end();
initializer_list
对象中的元素永远是常量值, 不允许改变 initializer_list
对象中元素的值- 使用
initializer_list
可以传递未知数量的同类型参数:
void errorMsg(initializer_list<string> ini_list) {
for (auto msg : ini_list)
cout << msg << endl;
}
- 想向
initializer_list
形参中传递一个值的序列,则必须把序列放在一对花括号内
errorMsg({string1, string2, ...});
- 含有
initializer_list
形参的函数也可以同时拥有其他形参
返回值
- 函数返回一个值的方式和初始化一个变量或形参的方式完全一样:返回的值用于初始化调用点的一个临时量, 该临时量就是函数调用的结果
- 不允许返回局部对象的引用或指针:函数终止意味着局部变量的引用或指针将指向不再有效的内存区域
- 调用一个返回引用的函数得到左值,其他返回类型得到右值
- 函数可以返回花括号包围的值的列表
函数重载
- 重载的函数应该在形参数量或形参类型上有所不同
- 顶层
const
不影响传入函数的对象,因此一个拥有顶层 const
的形参无法和另一个没有顶层 const
的形参区分开来
void print(string str);
void print(const string str);
void print(double *d_ptr);
void print(double *const d_ptr);
- 底层
const
的区别则可以区分为不同的函数
- 只能将
const
对象或指向 const
的指针传递给 const
形参 - 传递一个非常量对象或者指向非常量对象的指针时,编译器会优先选用非常量版本的函数
void print(string &str);
void print(const string &str);
void print(double *d_ptr);
void print(const double *d_ptr);
函数指针
- 函数的类型由它的返回类型和形参类型共同决定,与函数名无关
- 要声明一个指向函数的指针,只需要用指针替换函数名即可:
bool lengthCompare(const string &str1, const string &str2);
bool (*func_ptr1)(const string &, const string &);
bool (*func_ptr2)(const string &, const string &) = lengthCompare;
- 函数名即为函数的地址;把函数名作为一个值使用时,该函数名自动地转换成指针
- 可以直接使用指向函数的指针调用该函数,无须提前解引用指针
func_ptr1 = lengthCompare;
bool b1 = lengthCompare("hello", "bye");
bool b1 = func_ptr1("hello", "bye");
bool b1 = (*func_ptr1)("hello", "bye");