C++ Primer 第五版 第六章 函数

函数是一个命了名的代码块,我们通过调用函数执行相应的代码。

一、函数基础

一个典型的函数定义包括以下内容:返回类型、函数名字、由0个或多个形参组成的列表以及函数体。

函数的调用完成两项工作:一是用实参初始化函数对应的形参,二是将控制权转移给被调函数。此时,主调函数(calling function)的执行被暂时中断,被调函数(called function)开始执行。

函数的返回类型不能是数组类型或函数类型,但可以是指向数组或函数的指针。

1. 局部对象

形参和函数体内部定义的变量统称为局部变量

自动对象:普通局部变量对应的对象

局部静态对象(static类型):在程序的执行路径第一次经过对象定义语句时初始化,并且直到程序终止才被销毁,在此期间即使对象所在的函数结束执行也不会对它有影响。

2. 函数声明

类似于变量,函数只能定义一次,但可以声明多次。

函数声明也称作函数原型。函数的三要素(返回类型、函数名、形参类型)描述了函数的接口,说明了调用该函数所需的全部信息。

类似于变量,函数也应该在头文件中声明而在源文件中定义。把函数声明放在头文件中,就能确保同一函数的所有声明保持一致。而且一旦我们想改变函数的接口,只需改变一条声明即可。

定义函数的源文件应该把含有函数声明的头文件包含进去,编译器负责验证函数的定义和声明是否匹配。

3. 分离式编译

二、参数传递

每次调用函数时都会重新创建它的形参,并且传入的实参对形参初始化,

如果形参是引用类型,它将绑定到对应的实参上;否则,将实参的值拷贝后赋给形参。

使用引用避免拷贝,如果函数无须改变引用形参的值,最好将其声明为常量引用。

使用引用形参返回额外信息

const形参和实参

顶层const作用于对象本身。

当形参有顶层const时,传给它常量对象或者非常量对象都是可以的。

我们可以使用非常量初始化一个底层const对象,但是反过来不行。

将同样的初始化规则应用到参数传递上。

尽量使用常量引用

1. 数组形参

数组的两个特殊性质:①不允许拷贝数组; ②使用数组时(通常)会将其转换成指针。

当编译器处理对print函数的调用时,只检查传入的参数是否是const int*类型。

管理数组实参

①使用标记指定数组长度

要求数组本身包含一个结束标记。

eg. C风格字符串。C风格字符串存储在字符数组中,并且在最后一个字符后面跟着一个空字符。函数在处理C风格字符串时遇到空字符停止:

②使用标准库规范

传递指向数组首元素和尾后元素的指针。

③显式传递一个表示数组大小的形参

数组引用形参

2. main: 处理命令行选项

用户通过设置一组选项来确定函数所要执行的操作。

例如,假定main函数位于可执行文件prog之内,我们可以向程序传递下面的选项:

        prog -d -o ofile data0

这些命令行选项通过两个(可选的)形参传递给main函数:

        int main(int argc, char *argv[ ]) {...}

第二个形参argv是一个数组,它的元素时指向c风格字符串的指针;第一个形参argc表示数组中字符串的数量。因为第二个形参是数组,所以main函数也可以定义成:

        int main(int argc, char **argv) {...}

其中argv指向char*。

当实参传给main函数之后,argv的第一个元素指向程序的名字或者一个空字符串,接下来的元素一次传递命令行提供的实参。最后一个指针之后的元素值保证为0。

3. 含有可变形参的函数

C++有一种特殊的形参类型(即省略符),可以用它传递可变数量的实参。这种功能一般只用于与C函数交互的接口程序。

initializer_list 形参

    如果函数的实参数量未知但是全部实参的类型都相同,我们可以使用initializer_list类型的形参。initializer_list是一种标准库类型,用于表示某种特定类型的值的数组。initializer_list类型定义在同名的头文件中。

和vector一样,initializer_list也是一种模板类型。定义initializer_list对象时,必须说明列表中所含元素的类型:

和vector不同的是,initializer_list对象中的元素永远是常量值,我们无法改变initializer_list对象中元素的值。

省略符形参

三、返回类型和return语句
1. 无返回值函数

返回void的函数不要求非得有return语句,因为在这类函数的最后一句会隐式地执行return。

2. 有返回值函数

只要函数地返回类型不是void,则该函数内的每条return语句必须返回一个值。return语句返回值的类型必须与函数的返回类型相同,或者能隐式地转换成函数的返回类型。

列表初始化返回值

函数可以返回花括号包围的值的列表。

main函数的返回值可以看做是状态指示器。返回0表示执行成功,返回其他值表示执行失败,其中非0值得具体含义依机器而定。为了使返回值与机器无关,cstdlib头文件定义了两个预处理变量,我们可以使用这两个变量分别表示成功与失败:

3. 返回数组指针

如果我们想定义一个返回数组指针的函数,则数组的维度必须跟在函数名字之后。返回数组指针的函数形式如下所示:

Type表示数组元素的类型,dimension表示数组的大小。

四、函数重载

如果同一作用域内的几个函数名字相同但形参列表不同,我们称之为重载函数。

const_cast和重载

1.重载与作用域

五、特殊用途语言特性
1.默认实参

一旦某个形参被赋予了默认值,它后面的所有形参都必须有默认值。

如果我们想使用默认实参,只要在调用函数的时候省略该实参就可以了。

 函数的后续声明只能为之前没有默认值的形参添加默认实参,而且该形参右侧的所有形参必须都有默认值。

默认实参初始值

2. 内联函数和constexpr函数

调用函数一般比求等价表达式的值要慢一些。在大多数机器上,一次函数调用其实包含着一系列工作:调用前要先保存寄存器,并在返回时恢复;可能需要拷贝实参;程序转向一个新的位置继续执行。

内联函数可避免函数调用的开销

将函数指定为内联函数(inline)(在函数的返回类型前面加上关键字inline),通常就是将它在每个调用点上“内联地”展开。

constexpr函数

constexpr函数是指能用于常量表达式的函数。

定义constexpr函数要遵循:函数的返回类型及所有形参的类型都是字面值类型,而且函数体中必须有且只有一条return语句:

内联函数和constexpr函数通常定义在头文件中。

3.调试帮助
assert预处理宏

assert宏使用一个表达式作为它的条件:

assert宏定义在cassert头文件中。

NDEBUG预处理变量

assert的行为依赖于一个名为NDEBUG的预处理器变量的状态。如果定义了NDEBUG,则assert什么也不做。默认状态下没有定义NDEBUG,此时assert将执行运行时检查。

六、函数指针

使用函数指针

函数指针形参

形参可以指向函数的指针。

可以使用类型别名简化使用了函数指针的代码。decltype(函数名)返回函数类型,不会将函数类型自动转换成指针类型,所以要在结果前加*才能得到指针。

返回指向函数的指针

和数组类似,虽然不能返回一个函数,但是能返回指向函数类型的指针。

  • 18
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值