06
-
同表达式类似 尽管实参与形参有对应关系 但是并没有规定实参的求值顺序
-
返回值不能是数组或者函数 但是可以是数组的指针或者引用 或者是函数的指针
-
自动对象 形参在函数终止是被销毁
-
static局部静态对象 程序
第一次
经过对象定义语句时初始化 然后知道程序结束被销毁
局部静态对象没有显示初始化时会执行值初始化
-
在头文件中进行函数声明
-
形参建议使用引用代替指针 可以避免拷贝 无需改变形参时 可以使用const
可以利用额外的引用形参当作返回值 -
不能根据顶层的const进行函数重载
-
允许使用字面初始值初始化常量引用
-
数组形参
数组形参中的维度其实是没用的
比如下面的那个第三行的10其实没什么意义 这三个的定义是一样的
形参都是const int* 传进来的数组维度很有可能不是10void print(const int *); void print(const int[]); void print(const int[10]); // 一样会忽略第一个维度 所以下面的也都是一样的 void print(int arr[10][10], int row); void print(int arr[][10], int row); void print(int (*arr)[10], int row);
-
有几种方式表示数组长度
-
标记法 比如数组中的’a’就表示结束
-
使用标准库 比如传入两个迭代器使用 begin(ia) end(id)
-
传入长度
-
-
传递数组的引用
数组的大小是构成数组的一部分 所以传递引用时参数只能传递固定大小的数组
func1(int (&arr)[10]);
-
main参数
argc 程序运行的参数个数 argv 程序运行的参数
第一个参数必定是程序的输入int main(int argc, **argv) { return 0; }
-
如何传递不同数量甚至不用类型的实参
-
实参类型相同 c++11提供了名为
initializer_list
的标准库类型 -
实参类型不同 可以用模板
-
特殊形参省略符 多用于与C函数交互的接口程序
-
initializer_list
可以用于表示某种特定类型的值的数组 与vector比较像但是initializer_list中的值永远是常量值无法改变
形参定义为initializer_list
时 实参可以直接使用{}的列表 例子如下int t(initializer_list<int> v) { for (auto &vv : v) { cout << vv; } return 0; } int main() { t({1, 2, 3}); return 0; }
-
-
省略符号形参
...
是为了便于c++程序访问某些特殊的c代码设置的 这些代码使用了名为varargs的c标准库功能
foo(param_list, …)
foo(…) -
返回值为void的函数会在末尾隐式执行
return;
如果想中途返回中途return就好
返回值为void的函数不能返回其他类型 只能是return;
或者return 其他返回void的函数
-
往往在for循环之后没有返回值是不能被编译器发现的
-
不能返回局部对象的指针或者引用
-
列表初始化返回值
比如可以返回 {} {1,2,3,} {“aa”, “bb”} 这种 因为可以用这样的初始化列表对表示函数返回的临时变量进行初始化
所以返回{}时 返回类型执行值初始化 返回非空列表时 返回啥就看返回类型了 -
main函数允许没有return 因为不写的话 编译器会隐式的插入一条return 0;的语句
-
在cstdlib中定义了 EXIT_FAILURE 和 EXIT_SUCCESS 两个预处理器变量
-
返回数组指针的四种方式
-
正常返回 这就比较麻烦了
定义数组的引用或者指针是这样的 同理函数要返回数组的引用或者指针时
你可以想像将 函数function的出现位置 就是函数return值的出现的位置 就是p或者a的位置
所以函数的写法是这样的 很麻烦
Type (*function(param_list))[dimension]
int (*p)[10]
int (&a)[10] -
using 或者 typedef 简化 这个就简单些了
using arrT1 = int[10] typedef arrT2 = int[10] arrT1 *func(int i);
-
使用auto进行尾置返回 这个也很方便 注意
(*)
括号不能少auto func() -> int(*)[10]
-
decltype 也可以 但是 decltype 不会进行数组像指针的转换 decltype得到的就是数组类型 所以需要加一个
*
号int arr[10] = {}; decltype(odd) *func() {}
-
-
函数重载
之前有说 形参顶层const会被忽略 无法当作重载区别
函数的返回类型也无法当作重载的区别 -
内层作用域中的名字会隐藏外层作用域的名字 所以在不同的作用域中无法重载函数声明
-
名字查找发生在类型检查之前
-
在给定的作用域中一个形参只能被赋予一次默认形参 后续声明只能为之前没有默认实参的形参添加默认值
-
通常 默认实参应该在声明中指定 并将该声明放在合适的头文件中
-
局部变量不能做为默认实参
-
默认实参的名字在函数声明的作用域内解析 名字的求值发生在函数调用时
这就会发生如下的一些解释行为int m = 1; int n = 2; int t(int a = m, int b = n) { cout << a << " " << b << endl; return 0; } int main() { m = 11; int n = 22; t(); return 0; } 输出结果 11 2 a 为 11 因为第二部分 求值发生在调用时 在调用之前设置了m值为11 b 为 2 因为第一部分 名字的查找在函数的作用域内解析 调用t传递的默认n是全局的n的值 而不是main内的局部变量n的值
-
内联函数 在返回类型前增加 inline 关键字 会在调用函数的地方进行展开 可以减少函数调用开销
inline只是对编译器的请求 编译器其实可以选择忽略这个请求 -
constexpr函数
-
能够用于常量表达式的函数
-
函数的所有形参和返回类型都必须是字面值类型 (01-02有说 目前的话只有 算数类型 引用 指针)
-
constexpr函数是内联的
-
constexpr函数返回的并不一定是常量表达式
把constexpr函数用在需要常量表达式的地方时(比如数组维度)时编译器会检查函数结果是否符合要求constexpr size_t new_sz() {return 42;} constexpr size_t scale(size_t cnt) {return new_sz() * cnt;} // 当传递给 scale 的实参是 常量表达式时 返回值也是常量表达式 反之则不然 int arr[scale(2)]; // 合法 int i = 2; int arr[scale(i)]; // 不合法
-
-
内联函数和constexpr函数放在头文件内
虽然inline和constexpr函数允许在程序中多次定义 但是他的多个定义必须完全一致
并且编译器在函数调用的地方进行内联展开也是需要定义的
所以inline和constexpr函数通常放在头文件内定义 通常inline加在定义前 -
assert 预处理宏 assert(expr) 如果expr表达式的结果为假 则输出程序信息终止执行 为真则什么都不做
assert定义在cassert头文件中 -
NDEBUG预处理器变量
assert的行为依赖于NDEBUG预处理器的状态 定义了NDEBUG则assert什么也不做 -
一些程序调试用的名字
-
__func__
当前函数名字 -
__FUNCTION__
也是当前函数名字 -
__FILE__
文件名 -
__LINE__
当前行号 -
__TIME__
编译时间 -
__DATE__
编译日期
-
-
函数指针
-
把函数名作为一个值使用时 该函数自动转换成指针
-
函数指针使用无需提前使用解引用
-
对函数指针赋值时 直接使用函数名 或者对函数名取地址 效果是一样的
bool compareT1(int a, int b) { cout << a << " " << b << endl; return true; } // 使用 ----------- // 函数指针 bool (*pf1)(int a, int b); // 未初始化 bool (*pf2)(int a, int b); // 未初始化 pf1 = compareT1; pf2 = &compareT1; //取地址符是可选的与上一行等价 // 函数指针的使用也无需提前解引用 当然解引用也可以 pf1(2, 3); (*pf2)(4, 3); compareT1(6, 3);
-
函数指针做参数时 将函数类型当作形参会如上所属自动转换为函数指针 以下两处定义是等价的
-
可以使用typedef或者decltype自动简化
bool compareT1(int a, int b) { cout << a << " " << b << endl; return true; } // 虽然第三个参数是函数类型 但是会自动的转换成函数参数 // 这俩其实是等价的声明 // 下面用了typedef之后也有同样的例子 void use1(int, int, bool pf(int, int)); void use1(int, int, bool (*pf)(int, int)); // func1 func2 都是函数类型 typedef bool func1(int, int); typedef decltype(compareT1) func2; // pfunc1 pfunc2 都是函数指针类型 // decltype 得到的是函数类型 函数指针类型需要加一个*号 typedef bool (*pfunc1)(int, int); typedef decltype(compareT1) *pfunc2; // 这俩是等价的声明 // 注意这里的func1形参会自动转化为函数指针 就和上面的例子一样 void use2(int, int, func1); void use2(int, int, pfunc1);
-
返回指向函数类型的指针 和之前数组返回类似四种方式
要特别注意的是 返回类型不会自动转换为指针bool compareT1(int a, int b) { cout << a << " " << b << endl; return true; } // 直接返回 从内到外看看 // func是一个函数 参数是int 返回值是一个指针 // 发现返回的指针类型也带形参列表 所以返回值是一个函数指针 // 所以函数func 接受一个int 但会一个接受两个int返回bool的函数指针 // 其实可以笔画一下看成这样 bool (*返回值ret)(int, int); 就很容易看出来返回值ret是什么类型了 // 或者可以这样 bool (*pf)(int, int); 明显pf是一个函数指针 然后将pf换成其他函数 也很容易看出其他函数的返回类型了 bool (*func(int))(int, int); // 使用typedef typedef bool func1(int, int); typedef bool (*pfunc1)(int, int); // 这俩f1定义完全一样 pfunc1 f1(int) { return nullptr; } func1 *f1(int) { return nullptr; } // 这个会error 返回类型不会自动转换为指针 // func1 f1(int); // 尾置返回方式 auto func2(int) -> bool (*)(int, int); // decltype方式 decltype返回的是函数类型 记得要加上*号 decltype(compareT1) *func3(int);
-
-
函数匹配
-
类型相同
-
数组或者函数转换为对应指针
-
const转换
-
类型提升(整型提升)
-
算数类型转换
-
类类型转换
-