6.1.1 局部对象
对象的声明周期是指程序执行过程中该对象存在的一段时间。
对于只存在于块执行期间的对象称为自动对象。
局部静态对象:static int 这种来定义局部变量,只在程序第一次经过定义语句时候初始化,其他时候再次定义也不会有影响,同时出了函数块也不会销毁,直到程序终止才销毁。
例如:
int repeat() {
static int x = 0;
return x++;
}
一直反复调用这个程序,将会输出012345... 可以看出x并没有随函数结束而终止。
6.1.2函数声明
函数只能运行一次,但是可以定义多次。
函数声明也被称为函数原型。
函数应该在头文件中声明,在源文件中定义。
6.1.3 分离式编译
可以将主程序和函数分成两个文件分开编译,编译成目标文件后,链接生成可执行文件。
PS:这个书上的实验在我的mac电脑上没有跑通。
6.2.1 传值参数
值传递本质是实参的值拷贝给了形参,形成两个互相独立的对象。
引用传递的形参它对应实参的别名,因此可以在函数内改变实参的值。
通过向函数传递指针,从而达到修改实参的目的,但是函数内的对指针的修改是没有作用的,对指针所值对象的修改可以影响函数外实参的值。
6.2.2 传引用参数
不仅仅是需要改变实参时使用引用传递,有时候传入的string过长,不好拷贝时候,也应该用引用传递,这时候应该使用对常量的引用(const string &s),对常量的引用不能修改所绑定对象的值。
6.2.3 const形参和实参
const int &r = 42 这是对常量的引用,合法的,
int & r = 42 这是非法的
如果不打算改变形参的值,那么形参最好设置成对常量的引用,如果需要修改,再设置成普通引用。
6.2.4 数组形参
因为不允许拷贝数组,因此向函数传递数组时,其实传递的是数组首元素的指针
要注意下面这两个的区别:
int* matrix[10] //这是存放了十个指针的数组
int (*matrix)[10] //这是指向长度为10的数组的指针
6.2.6含有可变形参的函数
initializer_list 也是模板,例如initializer_list<string>lst
把initializer_list放在形参里,这样在实参里就可以传递任意数量的string了
操作和vector类似,但是无法改变initializer_list里的数值。
6.3 返回类型和return语句
如果函数返回一个应用,那么这个引用是所指对象的一个别名,也就是说,这种情况下函数可以成为左值参与运算。但是要注意不要返回局部变量的引用或者指针,因为函数结束后局部变量就会被销毁,这时候就没有指向的对象了。
现在可以返回一个{}花括号的列表了,就像这样:
vector<int> test{
return {1,2,3,4);
}
6.4 函数重载
同一作用域内函数名相同但是形参列表不同,称为重载函数
注意:其他都一样但是只有返回类型不同的重载函数是错误的
函数重载无法区分出顶层const的形参和没有顶层const的形参,底层const可以
如果在内层作用域内声明名字,将会隐藏外层作用域的同名声明,优先使用内层作用域的。
6.5.1 默认实参
实参列表中没有默认值的放前面,有默认值的放后面。
局部变量不能作为默认实参,但是函数作用域内对全局变量的修改仍然会对默认实参造成影响。
6.5.2 内联函数
如果这个函数比较小而且会被调用多次,那么可以把这个函数定义成inline内联函数。这样可以减少开销,系统会转成极简函数的样子。
inline int func(int a , int b = 3 ) {
return a + b;
}
6.6 函数匹配
如果重载时候,与被调用的函数重名且声明可见。那么称为候选函数。
形参数量与实参数量相等且实参类型和形参类型一样或者可转换,那么称为可行函数。
尽量不要类型转换,有类型转换的优先级低于没类型转换的。
6.7函数指针
函数指针可以指向函数,声明函数指针只需要把指针替换成函数名就行了,就像这样:
先定义一个函数:
int test1(int a, int b) {
return a + b;
}
然后设置指针指向他:
int (*textP) (int a, int b);
textP = test1;
注意这里最后一行的test1没有使用&符号,是因为函数名作为值使用的话会自动转换成指针。
使用decltype可以更方便的使用函数指针:
using func1 = decltype(test1);
vector<func1*> v;
v.push_back(test1);