C++Primer第5版 函数

                 函数的形参列表可以为空,但是不能省略,用逗号隔开,其中每个形参都含有一个声明符的声明,即使两个类型相同,都需要写各自类型。

main:处理命令行选项

        我们可以给main传递实参,int main(int argc , char* argv[]){};

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

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

WARING:当时用argv中的实参时,实参从argc[1]开始,argc[0]保存程序的名字,并非用户输入。

含有可变形参函数

为了能处理不同数量的实参的函数,C++11提供了两种主要方法,如果所有的实参类型相同,可以传递一个名为initializer list的标准库类型,如果实参的类型不同,我们可以编写可变的参数模板。

initalizer_list形参

如果实参数量为止但是全部实参的类型相同,我们可以使用initalizer_list类型的形参,用于表示某种特定类型的值的数组,它定义在同名的头文件中。

initializer_list lst://默认初始化,T类型的空列表

initializer_list lst{a,b,c,...},list的元素数量和初始值一样多,lst的元素是对应初始值的副本,列表中的元素是const

lst2(lst)//拷贝和赋值一个initalizer_list对象不会拷贝列表中的元素,拷贝后,原始列表和副本共享元素。

lst2=lst  与↑相同

lst.size()//元素数量

lst.begin//返回指向lst中首元素的指针

lst.end()//返回指向lst中尾元素下一位置的指针

定义initializer_list对象时,必须说明列表中所含元素的类型。对象中元素永远是常量值,无法改变。传递值的序列,需要放到一对花括号内。

省略符形参

是为了便于C++程序访问某些特殊的C代码设置的,这些代码使用了名为varargs的C标准功能。

WARNING:省略符形参仅仅用于C++和C的通用类型。大多数类类型的对象传递给省略符形参时都无法正确拷贝。

省略符形参只能出现在形参列表的最后一个位置。省略符形参所对应的实参无需类型检查。

返回类型和return语句

return语句终止当前正在执行的函数并将控制权返回到调用该函数的地方。

return语句有两种形式

return;

return expression;

无返回值的函数

没有返回值的return语句只能用在返回类型是void的函数中,返回void的函数不要求非得有return语句,因为在这类函数的最后一句后面会隐式的执行return。

一个返回类型是void的函数也能使用return语句的第二种形式,不过此时return语句的expression必须是另一个返回void函数,强行让void类型返回其它类型的表达式将产生错误。

有返回值的函数

return语句的返回值类型必须与函数的返回类型相同,或者能隐式的转换成函数的返回类型。

WARNING:在含有return语句的循环后面也应该有一条return语句,如果没有的话程序就是错误的 ,很多编译器无法发现此类错误。

如果函数返回指针,引用或类的对象,我们就能使用函数调用的结果访问结果对象成员。

函数的返回类型决定调用函数是否是左值,调用一个返回引用的函数的到左值,其他返回类型得到的是右值,可以像使用其他左值那样来使用返回引用的函数调用,我们能为返回类型为引用的函数结果赋值。常量引用不可赋值。

主函数main的返回值

允许main函数没有return语句直接结束,如果控制达到了main函数尾部而且没有return语句,编译器将隐式点的插入一条返回0的return语句。

递归:函数调用了它自身,称为递归函数。在递归函数中一定有某条路径是不包含递归调用的,否则函数将永远递归下去,函数将不断调用自身知道程序栈空间耗尽为止。

返回数组指针

数组不能被拷贝,所以函数不能返回数组,不过函数可以返回数组的指针或引用。

声明一个返回数组的指针

如果我们定义一个返回数组的指针的函数,则数组的维度必须跟在函数名字之后,然而函数的形参列表也在函数名字后面且形参列表应该先于数组维度。

type(*function(parameter_list)[dimension])

type是元素的类型,dimension表示数组的大小,(*function(parameter_list),两边的括号必须存在,如果没有,返回的将是指针的数组。

int(*func(int i))[10];

fun(int i) 表示调用fun函数式需要一个int类型的实参

(*fun(int i)) 意味着可以对函数调用结果实行解引用操作

(*fun(int i)[10] 意味着解引用func的调用将得到一个大小是10的数组

int(*func(int i))[10];表示数组中的元素是int类型。

使用尾置返回类型

任何函数都能使用尾置返回,但对于复杂的函数最有效,比如返回类型是数组的指针或者数组的引用。尾置返回来兴在形参列表厚棉并以一个->符号开头,表示函数真正的返回类型在形参列表市州,我们在本应该表示返回类型的地方方式一个auto。

auto func(int i)->int(*)[10]

decltype并不负责把数组类型转换成指针,所以decltype的结果是个数组。如果想声明需要显示的加一个*符号。

函数重载

同一作用域内函数名字相同但形参列表不同,称之为重载函数。main函数不能重载。

对于重载的函数来说,他们还在形参数量和形参类型上有所不同,与返回值的类型无关。

重载和const形参

顶层const不影响传入函数的对象。一个有顶层const和无const是等价的。

如果形参是某种类型的指针或引用,则通过区分其指向是常量对象还是费常量对象可以实现函数重载,此时的const是底层的.

当我们传递一个非常量对象或者指向非常量对象的指针时,编译器会优先选用非常量版本的函数。

调用重载的函数

定义了一组重载函数后,我们需要用合理的实参调用他们,函数匹配是指一个过程,在这个过程中我们把函数调用与一组重载函数中的某一个关联起来,函数匹配也叫作重载确定。

重载函数时有三种可能;

1.编译器找到一个与实参最佳匹配的函数,并生成调用该函数的代码。

2.找不到任何一个函数与调用的实参匹配,此时编译器发出无匹配的错误信息。

3.有多余一个函数可以匹配,但是每一个都不是明显的最佳选择,此时将发生错误,称为二义性调用。

重载与作用域

如果我们在内层作用域中声明名字,它将隐藏外层作用域中同名的实体,在不同的作用域中无法重载函数名。一旦在当前作用域中找到了所需的名字,编译器就会忽略掉外层作用域中的同名实体。

特殊用于语言特性

默认实参

在函数的很多次调用中它们都被赋予一个相同的值,我们把反复出现的值称为函数的默认实参,调用含有默认实参的函数时,可以包含该实参,也可以省略该实参。

默认实参作为形参的初始值出现在形参列表中,一旦某个形参被赋予了默认值,它后面的所有形参都必须有默认值。

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

在给定的作用域中一个形参只能被赋予一次默认实参,函数的后续声明只能为之前那些默认的形参添加默认实参,而且该形参右侧的所有形参必须有默认值。

只要表达式类型能转换成形参所需的类型,就能作为默认实参。

内联函数和constexpr函数

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

内联函数通常是在每个调用点上内联的展开。在函数前加上inline,声明为内联函数。

内联说明只是向编译器发出一个请求,编译器可以选择忽略这个请求。

constexpr函数

constexpr函数是指能用于常量表达式的函数,函数的返回类型以及所有的形参类型都是字面值类型,而且函数内只能有一条return语句。

执行初始化任务时,编译器把对constexpr函数调用替换成其结果值,constexpr被隐式的指定为内联函数。函数体内也可以包含其他语句,只要这些语句在运行时不执行任何操作就行。

允许constexpr函数返回值是一个非常量。

内联函数和constexpr函数可以再程序中多次定义,对于某个给定的 内联函数和constexpr函数来说,它的多个定义必须完全一致。

调试帮助

程序可以包含一些用于调试的代码,但是代码只在开发程序时使用,当应用程序编写完成准备发布时,要先屏蔽掉调试代码。这种方法用到两项预处理功能:assert和NDEBUG

assert预处理宏

assert是一种预处理宏。是一个预处理变量,使用一个表达式作为它的条件。

assert(expr)

首先对expr求值,表达式为假,assert输出信息并终止程序的执行,如果为真,它什么也不做。

定义在cassert头文件中,预处理的名字有预编译器管理,无需提供using声明,直接用就行。

宏名字在程序内必须唯一,含有cassert头文件的程序不能再定义名为assert的变量,函数或其它实体。很多头文件都包含了cassert。

NDEBUG预处理变量

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

我们可以定义一个#define DEBUG关闭调试。

定义NDEBUG能避免检查各种条件所需运行时的开销,不会执行运行时的检查,因此assert应该仅用于验证那些雀氏不可能发生的事情,我们可以吧assert当成调试程序的一种辅助手段,但是不能用它替代真正运行时的逻辑检查。也不能替代程序本身包含错误检查。

编译器为每个函数都定义了_ _func_ _,他是const char的一个静态数组,用于存放函数的名字。

预处理器还定义了4个对程序调用很有用的名字

_ _ FILE _ _ 存放文件名的字符串字面值

_ _ LINE _ _ 存放当前行号的整形字面值

_ _ TIME_ _ 存放文件编译时间的字符串字面值

_ _ DATE_ _ 存放文件编译日期的字符串字面值

函数匹配·

当几个函数及某些形参的类型可以由其他类型转换的得来时就不太容易了。

函数匹配的第一步时选定本次调用对应的重载函数集,集合中的集合称为候选函数。候选函数具备两个特征:1是与被调用的函数同名。2是声明在调用点可见。

第二步是考察实参,从候选函数中选出能被这组实参调用的函数。这些新选出的函数称为可行函数。可行函数也有两个特征,一是其形参数量与本次调用提供的实参数量相等,二是每个实参类型与对应的形参类型相同,或者能转换成形参的类型。

如果函数含有默认实参,则我们在调用该函数时传入的实参数量可能少于实际使用的是参数量。

实参数量初步判别了候选函数后,接下来考察实参的类型是否与形参匹配,匹配的含义是它们具有相同的类型,也可能是实参类型和形参类型满足转换规则。如果没有找到可行函数,编译器将会报错。

寻找最佳匹配

编译器选择那些形参数量满足要求,且实参类型和形参类型能够匹配的函数,接下来编译器一次检查每个实参以确定哪个函数是最佳匹配。如果有且只有一个函数满足下列条件,则匹配成功。

该函数每个实参的匹配都不劣于其他可行函数需要的匹配。

至少有一个实参的匹配优于其他可行函数提供的匹配。

实参类型转换

为了确定最佳匹配,编译器将实参类型到形参类型划分为几个等级。

1.实参类型与形参类型相同。

实参从数组类型或函数类型转换成对应的指针类型。

向实参中添加顶层const或者从实参中删除顶层const

2.通过const转换实现的匹配

3.通过类型提升实现的匹配

4.通过算数类型转换或指针转换实现的匹配。

5.通过类类型转换实现的匹配

小整数一般都会提升到int类型或更大的整数类型。

重载函数区别引用类型的形参是否引用了const,或者指针类型的形参是否指向const,编译器通过实参是否是常量决定哪个函数。

函数指针

函数指针指向的是函数而非对象,函数指针指向某种类型,函数类型由它的返回类型和i形参类型共同决定。与函数名无关。

当把函数名做为一个值使用时,该函数自动地转换成指针,能直接使用指向函数的指针调用函数,不用解引用。不同函数类型的指针间不存在转换规则。

重载函数指针,指针类型必须与重载函数中的某一个精确匹配。

函数指针形参

和数组类似,虽然不能定义函数类型的形参,但是形参可以是指向函数的指针。形参看起来是函数类型,实际上却是当成指针使用。

decltype返回函数类型,不会自动转换成指针类型。

返回指向函数的指针

虽然不能返回函数,但是能够返回指向函数的指针,我们必须把返回类型写成指针的形式,编译器不会自动把函数返回类型当成对应的指针类型处理。我们必须显示的将返回类型指定为指针。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值