第十三章---C++ 函数、递归、友元函数、函数指针和回调

news2024/10/5 6:23:54

函数是一组接受输入、执行一些特定计算并生成输出的语句。这个想法是将一些经常或重复完成的任务放在一起组成一个函数,这样我们就可以调用这个函数,而不是一次又一次地为不同的输入编写相同的代码。

简单来说,函数是仅在调用时运行的代码块。

函数:

函数的定义

一个基本的C++函数定义包括以下几个部分:

  • 返回类型:函数执行后返回的结果类型。
  • 函数名:用于标识函数的唯一名称。
  • 参数列表:函数执行时传入的数据,可以没有参数。
  • 函数体:花括号内的代码块,包含执行特定任务的语句。
    下面是一个简单的函数定义示例:
// 返回类型是int,函数名为add,有两个int类型的参数
int add(int a, int b) {
    return a + b; // 执行加法操作,并返回结果
}

函数的调用

定义函数后,可以在程序的其他部分调用它。调用函数时,需要提供相应的参数(如果有的话),并可以使用返回值(如果函数有返回类型的话)。

int result = add(5, 3); // 调用add函数,并接收返回值

函数的参数

函数的参数可以是多种类型,包括基本数据类型、复合数据类型、指针、引用等。

  • 值传递(Pass by Value):默认情况下,函数参数是通过值传递的,这意味着函数内部对参数的修改不会影响原始变量。
  • 引用传递(Pass by Reference):使用引用传递参数时,函数内部对参数的修改会影响原始变量。
  • 指针传递(Pass by Pointer):与引用类似,通过指针传递可以允许函数修改调用者的数据。

函数的返回类型

函数的返回类型可以是任何有效的C++数据类型,包括基本类型、复合类型、指针、引用等。如果函数不需要返回值,可以指定返回类型为void

特殊函数

  • main()函数:C++程序的入口点,每个程序都必须有一个main()函数。
  • 构造函数和析构函数:用于对象的创建和销毁。
  • 成员函数和静态成员函数:属于类的函数。

函数重载(Function Overloading)

C++允许定义多个同名函数,只要它们的参数列表(参数类型、数量或顺序)不同。这被称为函数重载。

int add(int a, int b);       // 第一个add函数
double add(double a, double b); // 第二个add函数,参数类型不同

默认参数

在C++中,可以为函数参数提供默认值。如果调用函数时没有为这些参数提供值,则使用默认值。

void print(int n, int base = 10) {
    // 如果base没有提供,则默认为10
}

内联函数(Inline Functions)

内联函数是建议编译器在调用点展开函数体的函数,以减少函数调用的开销。这通常用于小型、频繁调用的函数。

inline int max(int a, int b) {
    return (a > b) ? a : b;
}

函数模板

C++支持函数模板,允许编写与类型无关的代码。模板定义以关键字template开始,后跟一个或多个模板参数。

template <typename T>
T max(T a, T b) {
    return (a > b) ? a : b;
}

通过上述内容,我们可以看到C++中的函数非常灵活,它们是构建模块化、可维护和高效程序的基础。

递归:

递归是一种编程技巧,它允许函数调用自身。在C++中,实现递归的基本步骤如下:

  1. 递归基(Base Case):递归必须有一个或多个基例,这是递归终止的条件。没有基例,递归将会无限进行下去,最终导致栈溢出错误。
  2. 递归步骤(Recursive Step):在函数体内部,至少要有一个递归调用,它将问题分解为更小的相同问题。
  3. 递归调用:在递归步骤中,函数调用自身,但通常是在更小的输入或更接近基例的条件下。
    下面通过一个经典例子——计算斐波那契数列(Fibonacci sequence)的第n项——来展示如何实现递归:
#include <iostream>
// 函数声明
int fibonacci(int n);
int main() {
    int n;
    std::cout << "Enter the Fibonacci sequence number to calculate: ";
    std::cin >> n;
    
    // 输出斐波那契数列的第n项
    std::cout << "Fibonacci number is: " << fibonacci(n) << std::endl;
    return 0;
}
// 斐波那契数列的递归实现
int fibonacci(int n) {
    // 递归基:第0项是0,第1项是1
    if (n == 0) {
        return 0;
    } else if (n == 1) {
        return 1;
    }
    
    // 递归步骤:第n项是第n-1项和第n-2项之和
    return fibonacci(n - 1) + fibonacci(n - 2);
}

在这个例子中:

  • 递归基n == 0n == 1,对应斐波那契数列的前两个数。
  • 递归步骤return fibonacci(n - 1) + fibonacci(n - 2);,这里函数fibonacci调用自身,但是每次调用时的参数n都更小,逐渐接近基例。
    递归的优势在于它可以简化某些问题的解决方案,使其更易于理解和实现。

然而,递归也有缺点,例如:

  • 效率问题:如上面的斐波那契数列递归实现,会有大量的重复计算,效率较低。
  • 栈空间:每次递归调用都会在调用栈上占用空间,如果递归深度太大,可能会导致栈溢出。
    为了提高效率,通常会使用记忆化(Memoization)或动态规划(Dynamic Programming)等技术来优化递归算法。

友元函数(friend function):

在C++中,友元函数(friend function)是一种特殊类型的函数,它可以访问类的私有(private)和受保护的(protected)成员,即使这个函数不是类的成员函数。通常,友元函数用于操作类的私有数据成员,而无需将该操作暴露为类的公共接口。
要声明一个友元函数,你需要在类定义中使用 friend 关键字。以下是友元函数的几个要点和使用示例:

友元函数要点:

  1. 不是类的成员:友元函数定义在类的外部,不受类的作用域限制。
  2. 可以访问类的私有和受保护成员:友元函数可以访问类的所有成员,包括私有和受保护成员。
  3. 不需要通过对象调用:友元函数不通过对象来调用,它可以直接调用,就像普通函数一样。
  4. 友元关系是单向的:如果类A声明了类B的函数为友元,这并不意味着类B的函数也能访问类A的私有成员。
  5. 友元关系不可传递:如果函数f是类A的友元,并且类A是类B的友元,这并不意味着函数f是类B的友元。

示例:

以下是一个使用友元函数的示例,其中包含一个名为 Box 的类,该类有一个友元函数 printBoxSize,用于打印 Box 对象的尺寸。

#include <iostream>
class Box {
private:
    double width;
public:
    Box(double w) : width(w) {}
    // 声明printBoxSize为友元函数
    friend void printBoxSize(Box box);
};
// 定义友元函数
void printBoxSize(Box box) {
    std::cout << "Box width: " << box.width << std::endl;
}
int main() {
    Box box(10.0);
    // 直接调用友元函数,不需要通过对象
    printBoxSize(box);
    return 0;
}

在这个例子中,printBoxSize 函数能够访问 Box 类的私有成员 width,尽管它不是 Box 类的成员函数。这允许 printBoxSize 函数直接打印 Box 对象的宽度,而无需 Box 类提供公共接口来访问其私有成员。

在C++中,函数指针和回调是两个紧密相关的概念。以下是它们的详细解释:

函数指针和回调

函数指针

函数指针是一个指向函数的指针,就像普通指针指向变量一样。函数指针可以用于调用函数,也可以作为参数传递给其他函数,或者在运行时动态地选择要调用的函数。

定义函数指针

函数指针的定义方式如下:

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

例如,如果你有一个返回int并且接受两个int参数的函数,可以这样定义函数指针:

int (*funcPtr)(int, int);
使用函数指针

假设有一个函数:

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

你可以将add函数的地址赋值给funcPtr

funcPtr = &add;

或者更简单地:

funcPtr = add;

然后,你可以通过函数指针调用该函数:

int result = funcPtr(3, 4); // 调用 add(3, 4)

回调函数

回调函数是一种通过函数指针调用的函数。在C++中,回调函数通常用于在特定的事件或条件发生时执行代码。回调可以使程序设计更加模块化和灵活。

使用回调

以下是如何使用回调函数的示例:

#include <iostream>
// 回调函数类型
typedef void (*Callback)(int);
// 一个接受回调函数作为参数的函数
void performOperation(int a, int b, Callback callback) {
    int result = a + b;
    callback(result); // 调用回调函数
}
// 实现回调函数
void printResult(int result) {
    std::cout << "The result is: " << result << std::endl;
}
int main() {
    // 调用 performOperation 并传递 printResult 作为回调
    performOperation(5, 3, printResult);
    return 0;
}

在上面的例子中,performOperation函数接受两个整数和一个Callback类型的函数指针。printResult函数符合Callback类型,因为它接受一个整数并返回void。当performOperation计算结果后,它通过回调函数printResult打印结果。

函数指针和回调的用途

  1. 事件处理:在图形用户界面编程中,回调函数常用于响应用户操作。
  2. 排序算法:例如,qsort函数接受一个比较函数作为回调来确定元素顺序。
  3. 线程函数:线程可以执行一个函数,这个函数可以作为回调传递给线程函数。
  4. 异步编程:在异步操作完成后,回调函数可以用来处理结果。

理解函数指针和回调对于C++高级编程非常重要,它们允许程序员编写更通用、更灵活的代码。

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

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

相关文章

Armeria gPRC 高级特性 - 装饰器、无框架请求、阻塞处理器、Nacos集成、负载均衡、rpc异常处理、文档服务......

文章目录 定义一个示例高级特性装饰器概述简单案例多种装饰方式 无框架请求概述使用方式 阻塞任务处理器背景概述多种使用方式 rpc 异常统一处理使用方式更详细的异常信息 Armeria 提供 gRPC 客户端多种调用方式同步调用异步调用使用装饰器 负载均衡简单案例Armeria 提供的所有…

5G NR相关笔记

为了提供一致且准确的时序定义&#xff0c;NR规范了一个 基本时间单位 T c 1 / ( 480000 4096 ) , T_c1/(480 000\times 4096), Tc​1/(4800004096),所有与5GNR相关的时间的定义都被描述为这个基本时间单位的整数倍。基本时间单位 T c T_c Tc​ 因此可以看成是子载波间隔480…

10.2 Linux_进程_进程相关函数

创建子进程 函数声明如下&#xff1a; pid_t fork(void); 返回值&#xff1a;失败返回-1&#xff0c;成功返回两次&#xff0c;子进程获得0(系统分配)&#xff0c;父进程获得子进程的pid 注意&#xff1a;fork创建子进程&#xff0c;实际上就是将父进程复制一遍作为子进程&…

【AIGC】2023-ICCV-使用 Transformer 的可扩展扩散模型

2023-ICCV-Scalable Diffusion Models with Transformers 使用 Transformer 的可扩展扩散模型摘要1. 引言2. 相关工作3. 扩散 Transformer3.1 准备工作3.2 扩散 Transformer 设计空间 4. 实验设置5. 实验5.1 最先进的扩散模型5.2 缩放模型与采样计算 6. 结论参考文献 使用 Tran…

Ubuntu24.04远程开机

近来在几台机器上鼓捣linux桌面&#xff0c;顺便研究一下远程唤醒主机。 本篇介绍Ubuntu系统的远程唤醒&#xff0c;Windows系统的唤醒可搜索相关资料。 依赖 有远程唤醒功能的路由器&#xff08;当前一般都带这个功能&#xff09;有线连接主机&#xff08;无线连接有兴趣朋友…

PostgreSQL技术内幕13:PostgreSQL通讯协议

文章目录 0.简介1.PG通讯协议1.1 消息格式1.2 消息交互流程1.2.1 启动流程1.2.2 简单查询流程1.2.3 扩展查询1.2.3.1 pipelining 1.2.4 取消流程1.2.5 结束流程1.2.6 copy流程1.2.7 错误和通知 0.简介 之前文章对于PG的内部模块做了一些介绍&#xff0c;接下来对PG和外部交互的…

GS-SLAM论文阅读笔记-MGSO

前言 MGSO首字母缩略词是直接稀疏里程计(DSO)&#xff0c;我们建立的光度SLAM系统和高斯飞溅(GS)的混合。这应该是第一个前端用DSO的高斯SLAM&#xff0c;不知道这个系统的组合能不能打得过ORB-SLAM3&#xff0c;以及对DSO会做出怎么样的改进以适应高斯地图&#xff0c;接下来…

【有啥问啥】SE(Squeeze-and-Excitation)架构详解

SE&#xff08;Squeeze-and-Excitation&#xff09;架构详解 在深度学习&#xff0c;特别是计算机视觉领域&#xff0c;卷积神经网络&#xff08;CNN&#xff09;的发展日新月异。为了进一步提升CNN的特征提取能力和模型性能&#xff0c;研究者们不断探索新的网络架构和组件。…

向量数据库|第1期|从零开始学习

向量数据库|第1期|从零开始学习 1、向量数据库中的基本概念 1.1 什么是余弦 余弦函数是一种三角函数&#xff0c;在直角三角形中&#xff0c;某个锐角的余弦为&#xff1a;临边与斜边的比值&#xff0c;如下图cosAb/c。引申到任意三角形中&#xff0c;即余弦定理&#xff1a;…

2024年7月大众点评全国酒吧前百名城市分析

在做一些城市分析、学术研究分析、商业选址、商业布局分析等数据分析挖掘时&#xff0c;大众点评的数据参考价值非常大&#xff0c;截至2024年7月&#xff0c;大众点评美食店铺剔除了暂停营业、停止营业后的最新数据情况分析如下。 分析研究的字段维度包括大众点评数字id、字母…

LSM6DSV16X基于MLC智能笔动作识别(3)----MEMS Studio训练数据

LSM6DSV16X基于MLC智能笔动作识别.3--MEMS Studio训练数据 概述视频教学样品申请源码下载硬件准备选择MEMS导入数据配置窗口长度和量程配置滤波器选择特征数据设备树生成决策树生成参考程序转换UCF文件 概述 MEMS-Studio是一套完整的桌面软件解决方案&#xff0c;专为开发嵌入…

认知杂谈98《抵御噪声干扰》

内容摘要&#xff1a; “能量掠夺”是指他人负面言行对我们情绪和心理状态的不良影响&#xff0c;使我们感到沮丧或愤怒。这种影响可能源于我们内心对自身价值认同的不坚定&#xff0c;以及过分在意他人的看法。 要避免能量掠夺&#xff0c;我们需要建立心理防线&#xff0c;学…

Xilinx Vitis IDE启动时失去响应的解决办法

在启动Xilinx Vitis IDE时&#xff0c;有时候会遇到卡死的情况&#xff0c;无论是直接启动还是从Vivado的菜单中启动都一样。参考Xilinx官网的解决办法&#xff1a;&#xff08;一直到2023.1版本都是可以解决的&#xff0c;之后的版本没测过。&#xff09; Widget (amd.com) …

Leetcode—279. 完全平方数【中等】

2024每日刷题&#xff08;169&#xff09; Leetcode—279. 完全平方数 实现代码 class Solution { public:int numSquares(int n) {vector<int> dp(n 1, n);dp[0] 0;dp[1] 1;for(int i 2; i < n; i) {for(int j 1; j * j < i; j) {dp[i] min(dp[i], dp[i -…

Oracle中ADD_MONTHS()函数详解

文章目录 前言一、ADD_MONTHS()的语法二、主要用途三、测试用例总结 前言 在Oracle数据库中&#xff0c;ADD_MONTHS()函数用于在日期中添加指定的月数。 一、ADD_MONTHS()的语法 ADD_MONTHS(date, n) 其中&#xff0c;date是一个日期值&#xff0c;n是一个整数值&#xff0c…

C语言高阶【2】--动态内存管理【2】--柔性数组(这是个全新的知识点,不想了解一下吗?)

本章概述 柔性数组总结C/C中程序内存划分彩蛋时刻&#xff01;&#xff01;&#xff01;&#xff01; 柔性数组 数组这个东西&#xff0c;我想大家应该都不陌生了吧。但是&#xff0c;柔性数组这个东西可能你是第一次听说。 柔性数组概念&#xff1a;在C99之前是没这个东西的…

基于Flux的文生高清图片

Flux模型生成的图片画质极佳&#xff0c;改进修复了手的问题&#xff0c;支持字体生成和排版&#xff0c;训练参数大&#xff0c;风格多样&#xff0c;分辨率弹性好&#xff0c;embedding通用性好&#xff0c;不需要输入负面提示词。 安装ComfyUI ComfyUI下载安装 下载和配置…

计算机网络:计算机网络概述 —— 网络拓扑结构

文章目录 网络拓扑总线型拓扑特点缺陷 星型拓扑特点缺陷 环型拓扑特点缺陷 网状型拓扑优点缺陷 树型拓扑特点缺陷 网络拓扑 网络拓扑指的是计算机网络中节点&#xff08;计算机、交换机、路由器等&#xff09;之间物理或逻辑连接的结构。网络拓扑定义了节点之间的布局、连接方…

方法重载(Overload)

前言 在前面的学习中&#xff0c;我们学到了重写(Override),这里我们主要进行重载(Overload)的介绍&#xff0c;同时对重写和重载的区别进行分析。 1. 重载(Overload) #方法重载 在同一个类中定义多个同名但参数不同的方法。我们称方法与方法之间构成方法重载 在Java中&…

【宽搜】1. 层序遍历模板讲解

题目描述 题目链接&#xff1a;N叉树的层序遍历 层序遍历流程 请仔细阅读下图&#xff1a; 根据上图的流程&#xff0c;下面再明确几个问题&#xff1a; 1. 为什么要使用队列&#xff1f; 队列是先进先出的数据结构&#xff0c;在数的层序遍历中&#xff0c;需要先将节点p…