有关C++中函数的查漏补缺
函数
函数基础
- 除了函数名,函数指针也可以使用运算符()运行函数
- 函数的类型指的是 ( 返回类型 + 形参类型 )
- 存在于语句块中的对象叫自动对象
- 在函数里面的对象叫局部对象,定义时不给初值就执行默认初始化(内置类型其值是未定义的,使用会报错,不用会警告)
- 局部对象定义时加个
static
就成局部静态变量了,不给初始值会执行值初始化(内置类型默认为0) - 函数的声明又叫函数原型
参数传递
-
传实参不用类型一模一样,能转换就行
-
可以传递数组(传的是首元素指针,所以形参的大小写不写无所谓)或函数(会转换为函数指针)
-
数组的引用作为形参,则必须指定大小了,而且传的时候也不能乱传了
-
有的类型不支持拷贝(比如IO),也就不能传值,所以只能传引用
-
对于形参为
const
的情况,实参不const
也行,但是函数里就别想改值了 -
对于形参为
const
引用的情况,使用引用只是为了避免拷贝的开销,仍然不可以更改值 -
如果指针是底层
const
的也就是它是指向const
值的指针,那么则不可以通过指针的方式来改,但是实际上是可以改的 -
非
const
的引用或指针形参会导致可接受参数范围的缩小,因为编译器不允许将真的不能改的东西传到可以改的地方 -
处理不同数量实参,如果类型相同,可以使用
initializer_list
做形参,类型不同的话则需要写一个可变参数模板 -
initializer_list
里的值都是const
,并且拷贝赋值操作并不是真的拷贝而是共用,相当于一个vector
,有size
,也有begin
和end
(不是迭代器而是指针),传实参的时候,实参要用大括号括起来 -
...
也可以用来处理不同数量实参,但一般跟C用,C++中的类传到这种形参一般都拷贝的不对
返回类型
- 不可以返回数组或函数,必须返回数组或函数的指针或引用
- 返回
void
的函数如果返回一个返回void
的函数没问题,要是其他值则不行 - 返回的值只要能隐式转换成函数返回类型就行
- 返回局部对象的引用或指针很不好,因为函数结束后,这个对象就没了
- 返回引用类型那么结果就是左值
cstdib
中的EXIT_SUCCESS
和EXIT_FAILURE
是预处理变量,与机器无关- 返回类型是数组指针的时候,不能在声明中忘了维度,维度放在参数列表后面
- C++11还能返回{}表示的列表,属于骚操作
- 返回类型太复杂还不想用
typedef
或者using
简化的话 就用尾置返回,也属于骚操作
函数重载
- 需要注意的是顶层
const
参数的const
会被忽略,所以有无顶层const
的两个函数不能算重载,而算重复定义 - 有无底层
const
可以算重载,因为确实这样更严格 - 重载函数的匹配对于实参有优先级的,具体的可以参考书籍,大体规则是精确>
const
>类型提升>类型转换>类类型转换
函数指针
- 函数指针指向函数,要求形参和返回类型精确匹配(一模一样,unsigned 和int都不行),加不加&无所谓
- 当赋予函数指针
nullptr
或者0
时,表示目前不指向任何函数,而不是代表没有类型 - 函数指针可以作为函数的形参,直接写函数名会自动转为指针
- 函数指针也能作为返回值,但是返回的时候必须显式指出是指针,这回编译器不给自动转换了
- 可以使用尾置返回类型来表示返回函数指针,属于骚操作
默认实参
- 一个作用域中一个形参只能定义一次默认实参,每次声明都可以赋予一个新的没有默认实参的形参,但要保证默认实参都在后面
- 局部变量当不了默认实参,可以转换为合适类型的表达式可以
内联函数
- 说白了就是让编译器更快处理,返回类型前加
inline
就行了,到底联不联交给编译器决定 - 内联函数可以手动定义多次,因为不手动编译器自己也做了好多次这种定义(因为就是在指定位置替换定义),所以编译器对内联函数比较宽容,但很显然这些定义必须都是一样的,因此最好放在头文件中,
constexpr
因为是内联函数所以也要遵守这个规则
constexpr
函数
- 返回类型和形参都是字面值类型,而且只有一条
return
(这样才能保证编译时知道结果),那么这种函数就可以赋给constexpr
表达式,因为这都是编译时候干的事,所以constexpr
默认设置为内联的 - 如果不加
constexpr
那么函数就算生成常量表达式也不会返回常量表达式,因为普通函数就是要运行才知道结果 - 但是加了
constexpr
函数可不一定返回常量表达式,当传递非常量,编译时就计算不出值了,也就返回非常量表达式了
assert
预处理宏
- 定义在
cassert
头文件中,其实也是预处理变量,像一个内联函数,assert(表达式)
对表达式求值,一旦为假,就终止程序,真的话就啥也不做,assert
干不干活依赖的是NDEBUG
这个预处理变量的状态,如果定义了就啥也不做,默认是没定义的 assert
可不是语法糖,因为只要定义NDEBUG
,就真的不干活了,程序就可能会出问题,它只是用来做正式发布后就不做的调试- 可以使用
NDEBUG
和#ifndef
和#endif
来创建调试的代码,在命令行输入-D NDEBUG
则之间的代码就不运行了 - C++编译器提供了
__func__
静态局部变量来输出当前函数的名字,__FILE__
,__LINE__
,__TIME__
,__DATE__
分别表示文件名,行数,编译时间,日期,用来为调试输出信息