C++ Primer 第6章 函数(1)

6.1 函数基础

函数是一个命名了的代码块,我们通过调用函数执行相应的代码。函数可以有0个或多个参数,而且(通常)会产生一个结果。
一个典型的函数定义包括以下部分:返回类型、函数名字、由0个或多个形参组成的列表以及函数体。
函数的调用完成两项工作:一是用实参初始化函数对应的形参,二是将控制权转移给被调用的函数。此时,主调函数的执行被中断,被调函数开始执行。
执行函数的第一步是(隐式地)定义并初始化它的形参。当遇到一条return语句时函数结束执行过程。return语句也完成两项工作:一个返回return语句中的值,二是将控制权从被调函数转移给主调函数。
形参与实参
实参是形参的初始值,实参的类型必须与对应的形参类型匹配,函数调用规定实参数量和形参数量一致。
函数的形参列表
形参列表可以为空,但不能省略,可以书写一个空的形参列表,也可以使用关键字void表示函数没有形参。
形参列表中的形参通常用逗号隔开,其中每个形参都是含有一个声明符的声明。
函数返回类型
大多数类型都能用作函数的返回类型。一种特殊的返回类型是void,表示函数不返回任何值。
函数的返回类型不能是数组类型或函数类型,但可以是指向数组或函数的指针。

6.1.1 局部变量

在C++语言中,名字有作用域,对象有生命周期。名字的作用域是程序文本的一部分,名字在其中可见;对象的生命周期是程序执行过程中该对象存在的一段时间。
形参和函数体内部定义的变量统称为局部变量,仅在函数的作用域内可见。
在所有函数体之外定义的对象存在于程序的整个执行过程中。此类对象在程序启动时被创建,直到程序结束才会销毁。
自动对象
我们把只存在于块执行期间的对象称为自动对象。当块的执行结束后,块中创建的自动对象的值就变成未定义的了。
形参是一种自动对象。函数开始时为形参申请存储空间,一旦函数终止,形参也就被销毁。
局部静态变量
局部静态变量在程序的执行路径第一次经过对象定义语句时初始化,并且直到程序终止才被销毁,在此期间即使对象所在函数结束执行也不会对它影响。可以将局部变量定义成static类型从而获得这样的对象。

6.1.2 函数声明

函数的名字也必须在使用之前声明,类似于变量,函数只能定义一次,但可以声明多次。
函数声明无须函数体,用一个分号替代即可。因为不包含函数体,所以也就无须形参的名字。
函数三要素(返回类型、函数名、形参类型)描述了函数的接口,说明调用该函数所需的全部信息。函数声明也称作函数类型。
函数应该在头文件中声明而在源文件中定义。定义函数的源文件应该把含有函数声明的头文件包含进来。

6.1.3 分离式编译

分离式编译允许我们把程序分割到几个文件中去,每个文件独立编译。
编译和链接多个源文件:fact函数位于fact.cpp文件中,main函数位于main.cpp中,在head.h头文件中对这两个函数进行声明,在main函数中调用fact函数。

6.2 参数传递

每次调用函数时都会重新创建它的形参,并用传入的实参对形参进行初始化。
如果形参是引用类型,它将绑定到对应的实参上;否则,将实参的值拷贝后赋给形参。当形参是引用类型时,我们说它对应的实参被引用传递或者函数被传引用调用
当实参的值被拷贝给形参时,形参和实参是两个相互独立的对象。我们说这样的实参被值传递或者函数被传值调用

6.2.1 传值参数

函数对形参做的所有操作都不会影响实参。调用函数时,实参的值被用来初始化形参,形参在函数执行的过程中改变,调用完函数之后,形参被销毁,实参的值不变。例如,调用fact(i)不会改变I的值。
指针形参
当执行指针拷贝操作时,拷贝的是指针的值。
在这里插入图片描述
调用reset函数之后,实参所指的对象被置为0,但是实参本身并没有改变。

6.2.2 传引用参数

引用形参绑定初始化它的对象,通过说那个引用形参,运行函数改变一个或多个实参的值。
在这里插入图片描述
在这里插入图片描述
调用reset函数时,j是实参,函数中i是引用形参,i与j绑定,仅仅是j的又一个名字,所以reset内部对i的使用即是对j的使用,j会改变。
使用引用避免拷贝
拷贝大的类类型对象或者容器对象比较低效,甚至有的类类型根本不支持拷贝操作。如果函数无须改变引用形参的值,最好将其声明为常量引用。
使用引用形参返回额外信息
一个函数只能返回一个值,然而有时函数需要返回多个值,引用形参为我们一次返回多个结果提供了有效途径。可以给函数传入一个额外的引用实参令其保存我们所需的信息。
在这里插入图片描述

6.2.3 const形参和实参

当用实参初始化形参时会忽略掉顶层const。当形参有顶层const时,传给它常量对象或者非常量对象都是可以的。
在这里插入图片描述
指针或引用形参与const
可以使用非常量初始化一个底层const对象,也就是说,对于常量引用或者指向常量的指针,可以用非常量来初始化,但反过来不行,一个普通的引用或者指针必须用同类型的对象初始化,不能用常量的初始化普通的。
在这里插入图片描述
将相同的初始化规则应用的参数传递上可得如下形式:
在这里插入图片描述
尽量使用常量引用
把函数不会改变的形参定义成(普通的)引用是一种比较常见的错误,这么做函数的调用者一种误导,即函数可以修改它的实参的值。此外,使用引用而非常量引用也会极大地限制函数所能接受的实参类型。例如,我们不能把const对象、字面值或者需要类型转换的对象传递给普通的引用形参。

6.2.4 数组形参

数组的两个特殊性质对我们定义和使用作用在数组上的函数有影响,这两个性质分别为:不允许拷贝数组以及使用数组时通常会将其转换成指针。
在这里插入图片描述
如果我们传给print函数的是一个数组,则实参自动地转换成指向数组首元素的指针,数组的大小对函数的调用没有影响。
以数组作为形参的函数也必须确保使用数组时不会越界。因为数组是以指针的形式传递给函数的,所以一开始函数并不知道数组的确切尺寸,调用者应该为此提供一些额外信息。管理指针形参有三种常用的技术。
使用标记指定数组长度
管理数组实参的第一种方法是要求数组本身包含一个结束标记,例如C风格字符串,存储在字符数组中并且最后一个字符后面跟着一个空字符。
在这里插入图片描述
这种方法适用于那些有明显结束标记且该标记不会与普通数据混淆的情况。
使用标准库规范
管理数组实参的第二种技术是传递指向数组首元素和尾元素的指针。
在这里插入图片描述
调用函数时要传入指向首元素和尾元素下一位置的指针,可以使用begin和end函数。
显式传递一个表示数组大小的形参
第三种管理数组实参的方法是专门定义一个表示数组大小的形参。数组j的大小可有end(j)-begin(j)获得。
在这里插入图片描述
数组形参和const
当函数不需要对数组元素执行写操作时,数组形参应该是指向const的指针。只有当函数确实要改变元素值的时候,才把形参定义成指向非常量的指针。
数组引用形参
形参也可以是形参的引用,引用形参绑定到数组上。(&arr两端的括号必不可少,若没括号,则将arr声明成了引用的数组)
在这里插入图片描述
这一用法也无形中限制了print函数的可用性,我们只能将函数作用在大小为10的数组。16.1.1节介绍给引用类型的形参传递任意大小的数组。

6.2.5 main:处理命令行选项

6.2.6 含有可变形参的函数

为了编写能处理不同数量实参的函数,C++11新标准提供了两种主要的方法:如果所有的实参类型相同,可以传递一个名为initializer_list的标准库类型;如果实参的类型不相同,我们可以编写一个特殊的函数,也就是所谓的可变参数模板(16.4节介绍)。还有一种特殊的形参类型(即省略符),可以用它传递可变数量的实参。
initializer_list形参
initializer_list是一种标准库类型,用于表示某种特定类型的值的数组。initializer_list类型定义在同名的头文件中。
在这里插入图片描述initializer_list对象中的元素永远是常量值,我们无法改变initializer_list对象中的值。
如果想向initializer_list形参中传递一个值的序列,则必须把序列放在一对花括号中:
在这里插入图片描述
在这里插入图片描述
含有initializer_list形参的函数也可以同时拥有其他形参。
省略符形参
省略符形参是为了便于C++程序访问某些特殊的C代码而设置的,这些代码使用了名为varargs的C标准库功能。
省略符形参只能出现在形参列表的最后一个位置,省略符形参所对应的实参无须类型检查,
在这里插入图片描述

发布了16 篇原创文章 · 获赞 11 · 访问量 631

猜你喜欢

转载自blog.csdn.net/qq_42820853/article/details/104569989