深蓝学院C++基础与深度解析笔记 第 6 章 函数

news2024/11/8 12:06:38

1. 函数基础

● 函数:封装了一段代码,可以在一次执行过程中被反复调用。

A、函数头:
● 函数名称 —— 标识符,用于后续的调用
● 形式参数 —— 代表函数的输入参数
● 返回类型 —— 函数执行完成后所返回的结果类型

B、函数体
● 为一个语句块( block ){ },包含了具体的计算逻辑
● 函数声明与定义
– 函数声明只包含函数头,不包含函数体,通常置于头文件中
– 函数声明可出现多次,在同一个作用域其实没必要啊,但函数定义通常只能出现一次(存在例外,内联函数)

C、 函数调用
– 需要提供函数名与实际参数
– 实际参数拷贝初始化形式参数, 类型即可
– 返回值会被拷贝给函数的调用者
– 栈帧结构
● 拷贝过程的(强制)省略
– 返回值优化
– C++17 强制省略拷贝临时对象
● 函数的外部链接

2. 函数详解

A、参数
● 函数可以在函数头的小括号中包含零到多个形参
– 包含零个形参时,可以使用 void 标记
– 对于非模板函数来说,其每个形参都有确定的类型,但形参可以没有名称
– 形参名称的变化并不会引入函数的不同版本甚至有没有名字都不重要,但是类型必须完全一致!
实参到形参的拷贝求值顺序不定, C++17 强制省略复制临时对象
● 函数传值、传址、传引用
● 函数传参过程中的类型退化:多为数组最高位会退化
● 变长参数
– initializer_list(推荐):原理是包含两个指针一个指向开头,一个指向结尾的下一个
在这里插入图片描述
– 可变长度模板参数
– 使用省略号表示形式参数(不建议)

● 函数可以定义缺省实参
如果某个形参具有缺省实参,那么它右侧的形参都必须具有缺省实参
– 在一个翻译单元中(一个cpp中 ),每个形参的缺省实参只能定义一次,声明或者定义中有一次即可,多次会报错
– 具有缺省实参的函数调用时,传入的实参会按照从左到右的顺序匹配形参
– 缺省实参为对象时,实参的缺省值会随对象值的变化而变化
● main 函数的两个版本
– 无形参版本

int main()
{
}

– 带两个形参的版本

int main(int argc, int *argv[])
{
 
}

argv[0]就是可执行程序名

  • argc—非负数,表示从程序运行的环境传递给程序的实参个数。
  • argv—指针,指向包含 argc+1个指针的数组的首元素。数组末元素为空指针,若其前面有任何元素,则它们指向空终止多字节字符串,表示从执行环境传递给程序的若干参数。若 argv[θ]不是空指针,或等价地 argc>0,则它指向表示用于调用程序的名称的字符串,或空字符串。

B、函数体
函数体形成域:
– 其中包含了自动对象(内部声明的对象以及形参对象)
– 也可包含局部静态对象
● 函数体执行完成时的返回
– 隐式返回,没有显示return返回,比如void
– 显式返回关键字: return

return; 语句
●return 表达式 ;return 初始化列表 ;

– 小心返回自动对象的引用或指针,不要返回函数内部定义的变量 ,static可以忽略
– 返回值优化( RVO )—— C++17 对返回临时对象的强制优化

C、返回类型
● 返回类型表示了函数计算结果的类型,可以为 void
● 返回类型的几种书写方式
– 经典方法:位于函数头的前部
– C++11 引入的方式:位于函数头的后部
– C++14 引入的方式:返回类型的自动推导
● 使用 constexpr if 构造 具有不同返回类型 的函数 “ ”
● 返回类型与结构化绑定( C++ 17 )

#include <iostream> 
struct Str{
    int x;
    int y; 
};

Str fun() {
    return Str{};
}
int main()
{
    auto & [v1,v2] = fun(); 
}

●[[nodiscard]] 属性( C++ 17 )
无用的代码:
在这里插入图片描述
需要保留返回值:

#include <iostream>  
[[nodiscard]] int fun(int a,int b) 
{
    return a+b;
}
int main() 
{
    int x = fun(2,3); 
}

3. 函数重载与重载解析(c++特有)

A、函数重载:
使用相同的函数名定义多个函数,每个函数具有不同的参数列表,可以是不同的参数个数或参数类型,但是 不能基于不同的返回类型进行重载,会被编译器认为是重新定义函数

#include <iostream>

int fun(int x)
{
return x+1;
}

double fun(double x)
{
return x+1;
}

int main()
{
std::cout <<fun(3)<< std::endl; 
}

B、编译器如何选择正确的版本完成函数调用 ?
在这里插入图片描述
– 参考资源: Calling Functions: A Tutorial

C、 名称查找

from chatgpt:
在C++中,查找(lookup)是指在代码中引用标识符(如函数名、变量名、类型名等)时,编译器确定标识符的声明或定义的过程。C++中的查找可以分为限定查找(qualified lookup)和非限定查找(unqualified lookup)两种。

  1. 限定查找(Qualified Lookup):
    **限定查找是指在标识符前加上作用域限定符(如命名空间、类名、对象名等),以明确指定标识符所在的作用域。**编译器只在指定的作用域中查找标识符的声明或定义。例如:

    namespace A {
        int x;
        void foo();
    }
    
    int main() {
        A::foo(); // 限定查找
        return 0;
    }
    

    在上述代码中,A::foo()使用了限定查找,它明确指定了函数foo()在命名空间A中定义。编译器只会在命名空间A中查找foo()的定义。

  2. 非限定查找(Unqualified Lookup):
    非限定查找是指在标识符前没有作用域限定符,编译器根据标识符的上下文和查找规则进行查找。 编译器将按照以下顺序进行查找:

   - 当前块作用域
   - 外层块作用域
   - 全局作用域
   - 命名空间作用域
   - 标准库作用域
   - 其他全局命名空间作用域

例如:

#include <iostream>

int x;

void foo() {
    std::cout << x << std::endl; // 非限定查找
}

int main() {
    int x = 5;
    foo(); // 非限定查找
    return 0;
}

在上述代码中,foo()函数中的x是一个非限定查找,编译器首先在当前块作用域中查找x,找到了函数内部的局部变量x,输出结果为5。如果在函数内部没有找到对应的变量x,编译器将会继续往外层块作用域、全局作用域等进行查找。

限定查找和非限定查找在C++中是用来控制标识符的可见性和作用域解析的重要机制。通过合理地使用限定查找和非限定查找,可以有效避免标识符冲突和提高代码的可读性和可维护性。

– 限定查找( qualified lookup )与非限定查找( unqualified lookup )
– 非限定查找会进行域的逐级查找 名称隐藏( —— hiding )
– 查找通常只会在已声明的名称集合中进行

– 实参依赖查找( Argument Dependent Lookup: ADL )
在这里插入图片描述

● 只对自定义类型生效

D、重载解析:在名称查找的基础上进一步选择合适的调用函数

– 过滤不能被调用的版本 (non-viable candidates)

● 参数个数不对
● 无法将实参转换为形参
● 实参不满足形参的限制条件

– 在剩余版本中查找与调用表达式最匹配的版本,匹配级别越低越好(有特殊规则)

● 级别 1 :完美匹配 或 平凡转换(比如加一个 const )
● 级别 2 : promotion(提升) 或 promotion 加平凡转换
● 级别 3 :标准转换 或 标准转换加平凡转换  

● 级别 4* :自定义转换 或 自定义转换 加平凡转换 或 自定义转换加标准转换
● 级别 5* :形参为省略号的版本
● 函数包含多个形参时,所选函数的所有形参的匹配级别都要优于或等于其它函数

4. 函数相关的其它内容

A、递归函数:在函数体中调用其自身的函数
– 通常用于描述复杂的迭代过程:示例
一定要留有出口,小心无限循环!

B、几种特殊函数:
内联函数(inline):把相对简单的函数在调用的地方展开
可以保证在多个翻译单元(.cpp)中引入同一个头文件函数,从程序级别的一处定义原则->翻译单元级别的一处定义原则,一次定义即可
constexpr 函数 (C++11 起 ):11和17标准不一样。需要满足既可以在编译期执行,也可以在运行期执行
在这里插入图片描述
consteval 函数 (C++20 起 ):只能在编译期求值,产生常量结果 ,能够提升效果,避免误调用

C、 函数指针
– 函数类型与函数指针类型:
函数类型:返回值类型 + 参数类型

int*a)[3];     //a是指针
int  *a [3];       //a是数组

函数是不能赋值的,数组也不能赋值,会 自动转化为指针类型。
– 函数指针与重载
– 将函数指针作为高阶函数参数
– 将函数指针作为高阶函数返回值
– 小心: Most vexing parse

D、补充:
chatgpt讲解的函数类型与函数指针类型:
在C++中,函数类型和函数指针类型是两个相关但不完全相同的概念。下面我将为您解释这两者之间的区别和使用方法。

  1. 函数类型(Function Type):
    函数类型指的是函数的签名,包括参数类型和返回值类型。函数类型可以用于声明函数、定义函数指针以及作为函数的参数或返回值类型。函数类型的语法格式如下:

    返回值类型 (*函数名)(参数类型1, 参数类型2, ...);
    

    例如,下面是一个函数类型为 int(int, int) 的示例:

    int add(int a, int b);
    

    这里的 add 是一个函数,它接受两个 int 类型的参数并返回一个 int 类型的值。可以通过声明函数指针来使用函数类型。

  2. 函数指针类型(Function Pointer Type):
    函数指针类型是指向函数的指针,它可以存储函数的地址并允许通过指针调用该函数。函数指针的声明方式与函数类型相似,但需要使用 * 来表示指针类型。函数指针类型的语法格式如下:

    返回值类型 (*指针变量名)(参数类型1, 参数类型2, ...);
    

    例如,下面是一个函数指针类型为 int(*)(int, int) 的示例:

    int (*ptr)(int, int);
    

    这里的 ptr 是一个函数指针,它指向一个接受两个 int 类型参数并返回一个 int 类型值的函数。可以通过将函数的地址赋给函数指针来使用它。

下面是一个示例,演示了如何声明函数类型、定义函数指针并使用函数指针调用函数:

#include <iostream>

int add(int a, int b)
{
    return a + b;
}

int main()
{
    // 声明函数类型
    using AddFunction = int(int, int);

    // 定义函数指针
    AddFunction *ptr = add;

    // 使用函数指针调用函数
    int result = ptr(3, 4);
    std::cout << "Result: " << result << std::endl;

    return 0;
}

在上述示例中,我们首先使用 using 关键字声明了一个函数类型 AddFunction,它与函数 add 的签名相匹配。然后,我们定义了一个函数指针 ptr,并将 add 函数的地址赋给它。最后,我们通过函数指针 ptr 调用了函数 add,并将结果打印出来。

总结:
函数类型用于描述函数的签名,可以用于声明函数、定义函数指针以及作为函数的参数或返回值类型。函数指针类型是指向函数的指针,用于存储函数的地址并允许通过指针调用函数。函数

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/683811.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Matplotlib---雷达图

1. 雷达图 fig plt.figure(figsize(6, 6))x np. linspace(0, 2*np.pi, 6, endpointFalse) y [83, 61, 95, 67, 76, 88]# 保证首位相连 x np.concatenate((x, [x[0]])) y np.concatenate((y, [y[0]]))# 雷达图 axes plt.subplot(111, polarTrue) axes.plot(x, y, o-, l…

记录正式环境测试环境【RedHat7编译升级redis7.0.9】--有关报错及解决

记录正式环境&测试环境【RedHat7 编译升级redis7.0.9】--有关报错及解决 &#x1f53b; 一、报错详情1.1 ⛳ 写在前面1.2 ⛳ 报错11.3 ⛳ 报错21.4 ⛳ 安装redis1.5 ⛳ 版本检查 &#x1f53b; 二、⛳ 总结 &#x1f53b; 一、报错详情 1.1 ⛳ 写在前面 &#x1f341; 升级…

甘特图神器大比拼——国内外7款经典工具评测

甘特图是一种重要的项目管理工具&#xff0c;它能够帮助我们规划、安排和跟踪项目的进度和任务。然而&#xff0c;市面上的甘特图工具众多&#xff0c;选择恰当的工具并不容易。在本文中&#xff0c;我们将为您介绍国内外7款经典的甘特图神器&#xff0c;并进行详细评测和比较。…

常见分布式事务

一、2PC 将事务的提交过程分为 资源准备 和 资源提交 两个阶段&#xff0c;准备阶段所有事务参与者都预留资源的成功与否&#xff0c;决定了第二阶段提交或回滚。 2PC 第一阶段&#xff1a;准备阶段 1.协调者向所有参与者发送事务内容&#xff0c;询问是否可以提交事务&#…

libevent(7)libevent中的事件event

一、事件状态 libevent有4种事件状态&#xff0c;分别是: initialized、pending、active、persistent。这4种状态的转换关系如下: 1、已初始化&#xff08;initialized&#xff09;&#xff1a;对应图中的non-pending状态&#xff0c;表示事件已经新建完成&#xff0c;但是还未…

细说设计模式

23种设计模式 &#xff08;一&#xff09;、创建型模式 对象实例化的模式&#xff0c;创建型模式用于解耦对象的实例化过程。 单例模式&#xff1a;某个类只能有一个实例&#xff0c;提供一个全局的访问点。工厂方法模式&#xff1a;一个工厂类根据传入的参量决定创建出哪一种…

AutoCMS全自动网站系统,供大家学习研究参考,商用请联系开发者!

AutoCMS全自动建站系统&#xff0c;只要一个关键词即可生成一个站&#xff0c;全自动建站、无需维护、无需数据库、无需安装&#xff0c;下载即用。支持在二级目录运行&#xff0c;丰富您的网站内容。 AutoCMS全自动网站使用说明 1.程序无需安装&#xff0c;解压该软件包之后&…

CSS的自定义属性var和JS的classList.toggle()方法,使用详细(css中var变量怎么应用)

简介&#xff1a;CSS中的var&#xff08;变量&#xff09;是CSS3中的新特性&#xff0c;用于定义可重用的值&#xff0c;类似于编程语言中的变量&#xff1b;它允许您在整个CSS文件中定义一个值&#xff0c;并在需要时使用该值。这样可以使CSS更加灵活和易于维护&#xff1b;cl…

VUE L 数据代理 ④

目录 文章有误请指正&#xff0c;如果觉得对你有用&#xff0c;请点三连一波&#xff0c;蟹蟹支持✨ V u e j s Vuejs Vuejs数据代理 什么是数据代理❓ 代理底层~ D e f i n e P r o p e r y DefinePropery DefinePropery V u e Vue Vue数据代理原理理解总结 文章有误…

第三方库介绍——libpng

文章目录 概述库版本的选择与下载安装zlib和libpng交叉编译交叉编译zlib库交叉编译libpng库 libpng的使用 概述 libpng是一款C语言编写的比较底层的读写PNG文件的跨平台的库。由于依赖于zlib库&#xff0c;所以在安装libpng时往往还需要安装zlib库。 库版本的选择与下载 zli…

积分等式与积分不等式

参考资料&#xff1a; B站 - 考研数学-积分不等式&#xff08;所有方法全归纳&#xff09;张宇基础和强化及习题册 积分等式 中值定理夹逼准则积分法 在这个专题中&#xff0c;有如下经验&#xff1a; 遇到 f ( x ) f(x) f(x)连续&#xff0c;应当想到变限积分 ∫ a x f ( t…

【软考网络管理员】2023年软考网管初级常见知识考点(6)- 虚拟局域网技术

涉及知识点 什么是虚拟局域网VLAN&#xff1f;VLAN的分类有哪些&#xff0c;IEEE802.1Q&#xff0c;VLAN的端口类型&#xff0c;VLAN信息的传递&#xff0c;软考网络管理员常考知识点&#xff0c;软考网络管理员网络安全&#xff0c;网络管理员考点汇总。 文章目录 涉及知识点…

单臂路由(RIP协议原理)

一、前言 1.1 本文为单臂路由&#xff08;RIP协议原理&#xff09;笔记 (供新手参考&#xff09; 通过RIP协议&#xff0c;交换机、单臂路由的工作原理和配置方法。 1.2 思科路由器设置ip设置 在将设备摆放完毕后&#xff0c;需要配置每台设备的IP&#xff0c;有如下三种基…

深入理解Java中的Lock和AQS

文章目录 前言正文一、Lock接口的定义二、ReentrantLock 的实现三、AbstractQueuedSynchronizer的实现3.1 AQS 中的加锁底层3.2 ReentrantLock中的 Sync 同步器3.3 NonfairSync 的实现3.4 FairSync 的实现3.5 公平锁和非公平锁的总结3.5.1 公平锁3.5.2 非公平锁 3.6 释放锁 前言…

突破边界:高性能计算引领LLM驶向通用人工智能AGI的创新纪元

AGI | AIGC | 大模型训练 | GH200 LLM | LLMs | 大语言模型 | MI300 ChatGPT的成功带动整个AIGC产业的发展&#xff0c;尤其是LLM&#xff08;大型语言模型&#xff0c;大语言模型&#xff09;、NLP、高性能计算和深度学习等领域。LLM的发展将为全球和中国AI芯片、AI服务器市场…

什么样的产品更适合做海外网红营销?

随着社交媒体和全球化的兴起&#xff0c;海外网红营销成为了一种非常受欢迎的推广方式。然而&#xff0c;并非所有产品都能够在海外市场成功借助网红营销实现推广目标。本文Nox聚星将和大家详细探讨什么样的产品更适合在海外进行网红营销&#xff0c;并提供相关的策略建议。 一…

汽车智能化进入赛点:城市NOA落地竞速,战至最后一公里

城市NOA的竞争正在加速进入落地阶段&#xff0c;6月即将结束&#xff0c;理想汽车计划在剩余几天内&#xff0c;在北京和上海开启城市辅助智能驾驶功能内测&#xff0c;并在下半年推出通勤智能驾驶功能。 其应用方法是&#xff0c;车主可用在日常使用中&#xff0c;基于智能化…

Linux系统编程(多进程编程深入1)

文章目录 前言一、进程参数和环境变量的意义二、子进程程序结束返回值到哪里去了&#xff1f;三、进程退出函数四、实际使用案例五、僵尸进程总结 前言 本篇文章我们深入的讲解多进程编程。 一、进程参数和环境变量的意义 进程参数和环境变量是两种不同的机制&#xff0c;但…

SuperMap GIS基础产品桌面GIS FAQ集锦(3)

SuperMap GIS基础产品桌面GIS FAQ集锦&#xff08;3&#xff09; 【iDesktop】如何获取倾斜摄影的边界线&#xff1f; 【解决办法】1、将倾斜摄影添加到球面场景&#xff0c;使用【三维分析】-【生成DSM】获取栅格数据集 2、使用【代数运算】功能&#xff0c;将大于0的栅格值统…

我做了10年的测试,由衷的建议年轻人别入这行了

两天前&#xff0c;有个做功能测试7年的同事被裁员了。这位老哥已经做到了团队中的骨干了&#xff0c;人又踏实&#xff0c;结果没想到刚刚踏入互联网“老龄化”大关&#xff0c;就被公司给无情优化了。 现在他想找同类型的工作&#xff0c;薪资也一直被压&#xff0c;考虑转行…