C++函数

1.函数

(1)形参和实参

实参是形参的初始值。第一个实参初始化第一个形参,第二个实参初始化第二个形参以此类推。尽管实参与形参存在对应关系,但是并没有规定实参的求值顺序。编译器能以任意可行的顺序对实参求值。实参的类型必须与对应的形参类型匹配,我们知道初始化过程中初始值的类型也必须与初始化对象的类型匹配。函数有几个形参,我们就须提供相同数量的实参。想要调用成功我们必须提供可以转换成对应类型的实参

(2)函数的形参列表

函数的形参列表可以为空,但是不能省略。要想定义一个不带形参的函数,最常用的办法是书写一个空的形参列表。不过为了与 C 语言兼容,也可以使用关键字 void 表示函数没有形参:

void f1()(/*...*/)            //隐式地定义空形参列表
 void f2( void ){/*...*/)       //显式地定义空形参列表

形参列表中的形参通常用逗号隔开,其中每个形参都是含有一个声明符的声明。即使两个形参的类型一样,也必须把两个类型都写出来:

 int f3( int v1, v2)(/*...*/)//错误 
 int f4( int vl , int v2){/*...*/}//正确

我们无法使用未命名的形参,所以形参一般都有个名字。偶尔,函数确实有个别形参不会被用到,则此类形参通常不命名以表示在函数体内不会使用它。(不命名实参也必须提供)

比如这个int f5(int v,int)

(3)函数的返回类型

c ++11中,后置返回类型。就是在函数声明和定义中,把返回类型写在参数列表之后。前面放 auto ,表示函数返回类型放到参数列表之后,而放在参数列表之后的返回类型是通过->开始的。

举个例子:

auto func(int a, int b) -> void
{
    return;
}

(4)内联函数

内联函数可避免函数调用的开销将函数指定为内联函数( inline ),通常就是将它在每个调用点上"内联地"展开。

在函数的返回类型前面加上关键字 inline ,这样就可以将它成内联函数了:

inline int func(int x, int y)
{
    return x + y;
}
​
int main()
{
    cout<<func(4,5)<<endl;
}

这样在编译过程中展开类似这样的形式:

cout<<4+5<<endl;从而消除了函数运行时的开销

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

一般来说,内联机制用于优化规模较小、流程直接、频繁调用的函数。很多编译器都不持内联递归函数,而且一个较复杂的函数也不大可能在调用点内联地展开。

  • 内联函数的定义一般放在头文件中,即使被多次包含也会被当成一个函数,而其他函数不能。(即使加上头文件防卫式声明依旧是编译不通过)

  • 内联展开是在编译时进行的,只有链接的时候源文件之间才有关系。所以内联要想跨源文件必须把实现写在头文件里。如果一个内联函数会在多个源文件中被用到,那么必须把它定义在头文件中

(5)函数重载

详见6.4函数重载

1.概念

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

2.定义

对于重载的函数来说,它们应该在形参数量或形参类型上有所不同。在上面的代码中,虽然每个函数都只接受一个参数,但是参数的类型不同。 ​ 不允许两个函数除了返回类型外其他所有的要素都相同。假设有两个函数,它们的形参列表一样但是返回类型不同,则第二个函数的声明是错误的:

Record lookup ( constAccount &);//这里表示任意的返回值
bool lookup ( constAccount ); //错误,与上一个函数相比只有返回类型不同

   main函数不能重载

名字修饰规则

编译器在编译时对函数名字的改写方式

C语言:简单只是在函数名字前增加下划线

C++:复杂,编译器将函数参数的类型放置在函数名字中,来保证函数重载时名字在底层的不同

(6)函数杂合写法

函数返回类型为void表示不反回任何类型,但我们可以调用另一个返回类型为void的函数作为返回值

void func1()
{
    ;
}
​
void func2()
{
    return;//这个可以
    //return func1();//这个也可以
    //return void;//这个不行
}

下面就是返回引用的相关问题:

我们可以看到tmp的地址和k的地址是一样的,这样会造成很大的隐患

如果改一下,k改成整型又会怎样? 

可以看到他们的地址不同,所以k只是tmp的值拷贝

(7)函数的默认实参

某些函数有这样一种形参, 在函数的很多次调用中它们都被赋予一个相同的值,此时, 我们把这个反复出现的值称为函数的默认实参。调用含有默认实参的函 数时,可以包含该实参,也可以省略该实参。 例如,我们使用string 对象表示窗口的内容。一般情况下,我们希望该窗口的高、 宽和背景字符都使用默认值。但是同时我们也应该允许用户为这几个参数自由指定与默认 值不同的数值。为了使得窗口函数既能接纳默认值,也能接受用户指定的值,我们把它定 义成如下的形式: typedef string : : size_type sz ; string screen(sz ht= 24 , sz wid = 80 , char backgrnd = '' ) ; 其中我们为每一个形参都提供了默认实参, 默认实参作为形参的初始值出现在形参列表 中。我们可以为— 个或多个形参定义默认值, 不过需要注意的是, 一旦某个形参被赋予了 默认值, 它后面的所有形参都必须有默认值。

使用默认实参调用函数

如果我们想使用默认实参,只要在调用函数的时候省略该实参就可以了。例如,screen 函数为它的所有形参都提供了默认实参,所以我们可以使用0 、1、2 或3 个实参调用该函数:

string window;
window= screen() ;//等价于screen(24 , 80 ,'' )
window = screen (6 6 ) ;//等价于screen(66 , 80 ,'' )
window= screen(66, 256);// screen(66,256 , ' ' )
window = screen (66 , 256,'# ' ) ;//screen(66 , 256 , '# ' )

函数调用时实参按其位置解析, 默认实参负责填补函数调用缺少的尾部实参(靠右侧位 置)。例如, 要想覆盖backgrnd 的默认值, 必须为ht 和wid 提供实参: window = screen(,,' ? ' );//错误:只能省略尾部的实参 window= screen( ' ? ' ) ;// 调用screen ( ' ? ', 80 ,' ')

需要注意,第二个调用传递一个字符值,是合法的调用。然而尽管如此,它的实际效果却 与书写的意图不符。该调用之所以合法是因为' ?' , 是个char, 而函数最左侧形参的类型 string : : size_type 是一种无符号整数类型, 所以char 类型可以转换成(参见4. 1 l 霎节, 第141 页)函数最左侧形参的类型。当该调用发生时, char 类型的实参隐式地转换 成string : : size_type, 然后作为height 的值传递给函数。在我们的机器上, ' ? ' 对 应的十六进制数是Ox3F , 也就是十进制数的63 , 所以该调用把值63 传给了形参height 。 当设计含有默认实参的函数时,其中一项任务是合理设置形参的顺序,尽量让不怎么 使用默认值的形参出现在前面, 而让那些经常使用默认值的形参出现在后面。

默认实参声明

对于函数的声明来说, 通常的习惯是将其放在头文件中,并且一个函数只声明一次,但是多次声明同一个函数也是合法的。不过有一点需要注意 函数的后续声明只能为之前那些没有默认值的形参添加默认实参, 而且该形参右侧的所有形参必须都有默认值。假如给定

//表示高度和宽度的形参没有默认值
string screen(sz , sz , char= '' );
//我们不能修改—个已经存在的默认值:
string screen(sz , sz , char='*' ) ; // 错误: 重复声明
//但是可以按照如下形式添加默认实参:
string screen(sz = 24 , sz = 80 , char); // 正确:添加默认实参

通常,应该在函数声明中指定默认实参,并将该声明放在合适的头文件中。

如果函数存在函数声明,只能在声明中指定默认实参,而不能在函数的参数列表中。

默认实参初始值

局部变量不能作为默认实参。除此之外,只要表达式的类型能转换成形参所需的类型, 该表达式就能作为默认实参:

// wd 、def 和ht 的声明必须出现在函数之外

sz wd = 80 ;

char def = '' ;

sz ht() ;

string screen(sz = ht() , sz = wd, char= def);

string window= screen( ) ; //调用screen(ht() , 80,'')

用作默认实参的名字在函数声明所在的作用域内解析,而这些名字的求值过程发生在函数 调用时:

void f2 ()
{
  def = ' *';// 改变默认实参的值
  sz wd = 100;// 隐藏了外层定义的wd , 但是没有改变默认值
  window = screen() ;// 调用screen(ht() , 80,' * ' )
}

我们在函数f2 内部改变了def 的值,所以对screen 的调用将会传递这个更新过的值。 另一方面,虽然我们的函数还声明了一个局部变量用于隐藏外层的wd, 但是该局部变品 与传递给screen 的默认实参没有任何关系。

参考书籍:

C++ Primer中文版 第5版作 者: (美)李普曼(Lippman,S.B.),(美)拉乔伊(Lajoie,J.),(美)默(Moo,B.E.) 著 王刚,杨巨峰 译,出版社: 电子工业出版社,ISBN: 9787121155352

本文是学习过程中参照C++primer结合自己的理解所写的笔记,如有纰漏还请指出,谢谢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值