热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

C++primer学习笔记第六章

第六章函数函数可以有0个或者多个参数,可以重载函数,也就是同一个名字可以对应不同的函数。1.函数基础函数包括返回类型,函数名
第六章 函数

函数可以有0个或者多个参数,可以重载函数,也就是同一个名字可以对应不同的函数。

1.函数基础

函数包括返回类型,函数名,形参列表,函数体。

**函数三要素是返回类型,函数名,形参列表,描述了函数的接口,**说明了调用该函数所需的全部信息。

调用函数时用调用运算符(),()内时实参列表,使用实参初始化函数的形参。

实参是形参的初始值,二者类型需要匹配(可能会发生隐式类型转换),数量需要一致。形参可以为空。

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

1.局部对象

C++中,名字有作用域,对象有生命周期。

函数是一个语句块,块构成一个新的作用域。所以形参和函数体内部的变量统称为局部变量,局部变量的生命周期依赖于定义的方式。(与之不同的是,函数体之外定义的对象存在于程序的整个执行过程,此类对象在程序启动时创建,程序结束时才会被销毁)

自动对象:普通局部变量对应的对象在函数控制路径经过变量定义的语句时创建该对象,到达块所在末尾时销毁它。块执行结束后,自动对象的值自动变成未定义。形参是自动对象。

局部静态对象:将变量定义成static类型,局部变量的生命周期贯穿于定义到整个程序终止。

static int a=0;

2.函数声明(函数原型)

函数只能被定义一次,但可以声明多次。如果一个函数用于不会用到,可以只声明不定义。

函数声明无需函数体,用分号代替即可。可以不写形参,但写上更易于理解。

和变量一样,建议函数在头文件中声明而在源文件中定义。含有函数声明的头文件应该被包含到定义函数的源文件中。

3.分离式编译

编译和链接多个源文件。如果我们只修改了其中一个源文件,只需重新编译那个改动改动了的文件。

分离式编译会产生后缀.obj(windoes)或者.o(UNIX)的文件,后缀名的含义式该文件包含对象代码。

2.参数传递

形参初始化机理和变量初始化一样。相当于用实参给形参赋值。

1.值传递/传值参数

**实参的值被拷贝给形参,函数对形参所做的操作不会影响到实参。**比如有个函数是 int fact(int a){ };然后调用fact(5),5的值虽然在fact函数中被改变了,但是实参的值5没有改变。

通过指针可以修改它所指对象的值。

//函数接受一个指针

void reset(int *ip){

*ip=0; //改变指针ip指向的对象的值

ip=0; //只改变了ip的局部拷贝,实参未被改变

}

//调用reset函数。实参所指对象被置为0,但是实参本身的值没变

int i=42;

reset(&i); //改变i的值而非i的地址,输出i=0。&应该是是取址吧

C中通常用指针类型的形参访问函数外部的对象,C++中建议用引用类型的形参代替指针。

2.引用传递/传引用参数

//函数接受一个引用

void reset(int &ip){

ip=0;

}

//调用reset函数。直接传入对象而不需要传入对象的地址

int i=42;

reset(i); //输出i=0。这种形参是引用简单得多啊!

函数无需改变引用形参的值的时候,最好用常量引用。

可以利用引用形参返回多个值。

3.const形参与实参

形参是const的时候,实参初始化的时候会忽略顶层const。和没有const的一样。

**形参尽量使用常量引用:**1.防止函数修改实参的值;2.使用普通引用会限制函数所能接受的实参类型,例如我们不能把const对象,字面值或者需要类型转换的对象传递给普通的引用形参。

4.数组形参

数组不能拷贝—无法以值传递使用数组参数

数组通常会被转换为指针—为函数传递数组时,实际上传递的是指向数组首元素的指针

数组是以指针形式传递的,要提供数组大小信息:1.使用标记指定数组长度;2.形参列表传递指向数组首元素和尾后元素的指针;3.形参列表除了数组,再定义一个size_t类型的表示数组大小的形参。

**形参可以是数组的引用。**int (&arr)[10],注意没有括号就是引用的数组。

传递多维数组:void(int (*matrix) [10],int rowsize)

5.含有可变形参的函数

有时候我么无法预知应该向函数传递几个实参。

1.initializer_list形参

函数实参类型相同但数量未知时。

initializer_list是一种标准库类型,表示某种特定类型的值的数组,与vector类似,它也是一种模板类型,定义时需要说明类型,含有begin和end成员函数。但是这个对象中的值永远是常量值。

2.省略符形参

仅仅用于C和C++通用的类型。

3.返回类型和return语句


1.无返回值函数

即return。一般用于void类型的函数中,而且void类型函数最后一句会隐式执行return

return可以用于void函数中的提前退出,类似于break。

void也可以是return 表达式,但是表达式的值必须是另一个返回void的函数。

2.有返回值函数

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

在含有return语句的循环后面必须有一条return语句。

返回的值用于初始化调用点的一个临时量,该临时量就是函数调用的结果。

**不要返回局部对象的引用或指针,**因为函数完成后它所占用的存储空间也会被释放掉,因此引用或指针将指向不再有效的内存区域。

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

main函数可以没有return语句直接结束,编译器会隐式插入一条return 0,0表示执行成功,非0值的含义依机器来定,也可以用cstdlib头文件中定义的预处理变量作为return值表示成功与失败。

3.递归函数

一个函数调用了自身。递归中一定有某条路径是不包含递归调用的,不然程序会一直执行下去。

这个函数递归终止的条件是val等于1。

int factorial(int val){

if(val>1)

return factorial(val-1)*val;

else

}

//计算一个数的阶乘

int factorial(int val){

if(val>1)

return factorial(val-1)*val;

return 1;

}

4.返回数组指针

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

注意返回类型和函数定义类型一致。

int (*func(int i)) [10]表示返回类型是数组的指针。

使用尾置返回类型:任何函数的定义都能使用。但对于复杂的函数比较有效,比如返回类型是数组的指针或者引用。函数真正的返回类型跟在形参列表之后。

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

或者使用decltype关键字。

4.函数重载

重载函数:同一作用域内几个函数名字相同但形参列表不同,称为重载函数。编译器会根据传递的实参类型推断想要的是哪个函数。比如一种数据库应用需要创建几个不同的函数分别根据名字,电话,号码等记录查询。

即返回类型,函数名相同,形参列表不同。

不允许返回类型不同,函数名相同,形参列表相同的两个函数出现。

main函数不能重载。

//这两个是相同的,形参的名字不影响形参列表的内容

record lookup(const account &acct);

record lookup(const account &);

//这两个相同,第二个是第一个的别名,别名并不创建新类型

typedef phone telno;

record lookup(const phone&);

record lookup(const telno&);

**重载和const形参:**顶层const是指针本身是个常量,底层const是指针所指对象是一个常量。

​ 顶层const的形参和没有顶层const的形参不可区分

record lookup(phone); record lookup(const phone);

record lookup(phone*); record lookup(phone* const);

​ 底层const的形参和没有底层const的形参通过区分其所指对象是常量还是非常量可以区分

record lookup(account&); record lookup(const account&);

record lookup(account*); record lookup(const account*);

重载不重载取决于是不是比原来容易理解。

重载和const_cast(显式类型转换)

**调用重载的函数:有三种结果,**最佳匹配,无匹配,二义性调用(多于1个函数可以匹配,但每一个都不是最佳选择)

重载与作用域:在内层作用域中声明函数名字,将会隐藏外层作用域中声明的同名实体。所以重载函数的声明都要放在同一个作用域内。

5.特殊用途语言特性


1.默认实参

函数形参可以有初始值,但是一旦某个形参被赋予了默认值,它后面所有的形参都必须有默认值。

调用函数如果想使用默认实参,调用的时候省略该实参就可以了。默认实参负责填补尾部的实参,所以设计的时候,尽量让不怎么使用默认实参的形参出现在前面,经常使用默认实参的出现在后面。

给定作用域中,一个形参只能被赋予一次默认实参。

局部变量不能作为默认实参。

2.内联函数和constexpr函数

一次函数调用包含很多工作,对于规模较小的函数,在其返回类型前添加inline关键字可以把函数指定为内联函数,消除调用的开销。

**constexpr是能用于常量表达式的函数,**其函数返回值和形参类型都要是字面值类型,且只有一个return语句。

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

3.调试帮助(assert和NDEBUG)

程序包含一些用于调试的代码,但只在开发的时候使用,发布时要屏蔽调试代码。

需要用到两项预处理功能:assert和NDEBUG.

assert是一种预处理宏,由预处理器而不是编译器管理,定义在cassert头文件中。

assert(表达式); 表达式为假,assert输出信息并终止程序,为真什么也不做。

assert的行为依赖于NDEBUG这个预处理变量的状态,使用#define语句定义NDEBUG可以关闭调试状态。

还可以用NDEBUG定义自己的调试代码,如果NDEBUG未定义,将执行#idndef和#endif之间的代码,否则这些代码会被忽略。

6.函数匹配

当重载函数形参数量相等,类型可以相互转换时:确定候选函数(同名,调用点可见)—确定可行函数(形参与实参数量相等(如果函数由默认实参,那么调用时传入的实参数量可以少于实际使用的实参),类型相等或者可以转换)—确定最佳匹配(实参形参类型越接近,匹配得越好,如果每个函数匹配都不好的话,编译器会报告二义性调用的错误)

如果重载函数的区别在于形参是否使用了引用了const,则编译器通过实参类型选择合适的函数。

7.函数指针

只需要用指针(*p)替换函数名即可。注意括号不能少,不然就是声明一个函数,返回int*类型这种。

好烦不想看这一部分了,后面用到


推荐阅读
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • 重入锁(ReentrantLock)学习及实现原理
    本文介绍了重入锁(ReentrantLock)的学习及实现原理。在学习synchronized的基础上,重入锁提供了更多的灵活性和功能。文章详细介绍了重入锁的特性、使用方法和实现原理,并提供了类图和测试代码供读者参考。重入锁支持重入和公平与非公平两种实现方式,通过对比和分析,读者可以更好地理解和应用重入锁。 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了C++中省略号类型和参数个数不确定函数参数的使用方法,并提供了一个范例。通过宏定义的方式,可以方便地处理不定参数的情况。文章中给出了具体的代码实现,并对代码进行了解释和说明。这对于需要处理不定参数的情况的程序员来说,是一个很有用的参考资料。 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • 本文介绍了如何使用JSONObiect和Gson相关方法实现json数据与kotlin对象的相互转换。首先解释了JSON的概念和数据格式,然后详细介绍了相关API,包括JSONObject和Gson的使用方法。接着讲解了如何将json格式的字符串转换为kotlin对象或List,以及如何将kotlin对象转换为json字符串。最后提到了使用Map封装json对象的特殊情况。文章还对JSON和XML进行了比较,指出了JSON的优势和缺点。 ... [详细]
  • MVC设计模式的介绍和演化过程
    本文介绍了MVC设计模式的基本概念和原理,以及在实际项目中的演化过程。通过分离视图、模型和控制器,实现了代码的解耦和重用,提高了项目的可维护性和可扩展性。详细讲解了分离视图、分离模型和分离控制器的具体步骤和规则,以及它们在项目中的应用。同时,还介绍了基础模型的封装和控制器的命名规则。该文章适合对MVC设计模式感兴趣的读者阅读和学习。 ... [详细]
  • 【重识云原生】第四章云网络4.8.3.2节——Open vSwitch工作原理详解
    2OpenvSwitch架构2.1OVS整体架构ovs-vswitchd:守护程序,实现交换功能,和Linux内核兼容模块一起,实现基于流的交换flow-basedswitchin ... [详细]
author-avatar
何丽-Hely
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有