热门标签 | 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*类型这种。

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


推荐阅读
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • 类加载机制是Java虚拟机运行时的重要组成部分。本文深入解析了类加载过程的第二阶段,详细阐述了从类被加载到虚拟机内存开始,直至其从内存中卸载的整个生命周期。这一过程中,类经历了加载(Loading)、验证(Verification)等多个关键步骤。通过具体的实例和代码示例,本文探讨了每个阶段的具体操作和潜在问题,帮助读者全面理解类加载机制的内部运作。 ... [详细]
  • 过去查询Mysql的时候,都见3306对所有端口开放着,感觉不安全。netstat -anlp | grep mysqltcp 0&am ... [详细]
  • 本文介绍了如何利用 `matplotlib` 库中的 `FuncAnimation` 类将 Python 中的动态图像保存为视频文件。通过详细解释 `FuncAnimation` 类的参数和方法,文章提供了多种实用技巧,帮助用户高效地生成高质量的动态图像视频。此外,还探讨了不同视频编码器的选择及其对输出文件质量的影响,为读者提供了全面的技术指导。 ... [详细]
  • Hadoop的文件操作位于包org.apache.hadoop.fs里面,能够进行新建、删除、修改等操作。比较重要的几个类:(1)Configurati ... [详细]
  • 如果应用程序经常播放密集、急促而又短暂的音效(如游戏音效)那么使用MediaPlayer显得有些不太适合了。因为MediaPlayer存在如下缺点:1)延时时间较长,且资源占用率高 ... [详细]
  • Spring Boot 中配置全局文件上传路径并实现文件上传功能
    本文介绍如何在 Spring Boot 项目中配置全局文件上传路径,并通过读取配置项实现文件上传功能。通过这种方式,可以更好地管理和维护文件路径。 ... [详细]
  • Linux CentOS 7 安装PostgreSQL 9.5.17 (源码编译)
    近日需要将PostgreSQL数据库从Windows中迁移到Linux中,LinuxCentOS7安装PostgreSQL9.5.17安装过程特此记录。安装环境&#x ... [详细]
  • 如何在Java中使用DButils类
    这期内容当中小编将会给大家带来有关如何在Java中使用DButils类,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。D ... [详细]
  • 在Delphi7下要制作系统托盘,只能制作一个比较简单的系统托盘,因为ShellAPI文件定义的TNotifyIconData结构体是比较早的版本。定义如下:1234 ... [详细]
  • 在使用Eclipse进行调试时,如果遇到未解析的断点(unresolved breakpoint)并显示“未加载符号表,请使用‘file’命令加载目标文件以进行调试”的错误提示,这通常是因为调试器未能正确加载符号表。解决此问题的方法是通过GDB的`file`命令手动加载目标文件,以便调试器能够识别和解析断点。具体操作为在GDB命令行中输入 `(gdb) file `。这一步骤确保了调试环境能够正确访问和解析程序中的符号信息,从而实现有效的调试。 ... [详细]
  • 2.2 组件间父子通信机制详解
    2.2 组件间父子通信机制详解 ... [详细]
  • 性能测试中的关键监控指标与深入分析
    在软件性能测试中,关键监控指标的选取至关重要。主要目的包括:1. 评估系统的当前性能,确保其符合预期的性能标准;2. 发现软件性能瓶颈,定位潜在问题;3. 优化系统性能,提高用户体验。通过综合分析这些指标,可以全面了解系统的运行状态,为后续的性能改进提供科学依据。 ... [详细]
  • 如何在PHP中准确获取服务器IP地址?
    如何在PHP中准确获取服务器IP地址? ... [详细]
  • 本文详细介绍了在Linux系统上编译安装MySQL 5.5源码的步骤。首先,通过Yum安装必要的依赖软件包,如GCC、GCC-C++等,确保编译环境的完备。接着,下载并解压MySQL 5.5的源码包,配置编译选项,进行编译和安装。最后,完成安装后,进行基本的配置和启动测试,确保MySQL服务正常运行。 ... [详细]
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社区 版权所有