C++基本知识 —— 缺省参数·函数重载·引用

C++基本知识 —— 缺省参数·函数重载·引用

  • 1. 缺省参数
  • 2. 函数重载
  • 3. 引用
    • 3.1 引用的基础知识
    • 3.2 引用的作用
    • 3.3 const 引用
    • 3.4 指针与引用的关系

1. 缺省参数

什么是缺省参数?缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数的时候,如果没有指定实参则采用该形参的缺省值(默认值),否则使用指定的实参。缺省参数也就做默认参数,缺省值一般都是字面量常量,也可以是全局变量;传参时实参只能从左向右传,且若传参时,只传了一个实参,那么函数的参数列中的第一个形参的值就是这个实参的值;缺省参数分为全缺省和半缺省参数。全缺省就是全部形参使用的都是缺省值。下面用具体的代码来解释上述概念:

// 这就是一个全缺省函数
void Func(int x = 10, int y = 20, int z = 30)
{cout << "x = " << x << "\ty = " << y << "\tz = " << z << endl;
}int main()
{//不传参 x y z 使用的都是缺省值Func(); //只传一个实参,那么 x 会使用指定的实参,y z 使用的是缺省值Func(1);//传两个实参,那么 x y 会使用指定的实参,z 使用的是缺省值Func(1, 2);//全部都传实参,那么 x y z 都会使用指定的实参Func(1, 2, 3);return 0;
}

运行结果:

在这里插入图片描述

C++规定在传实参时,不能间隔跳跃传参,只能从左到右连续的传,如下所示:

// 这样传参是错误的
Func(1, , 3);
Func(, 2, 3);

半缺省就是一部分的形参使用的是缺省值,且半缺省参数的缺省值得要从右往左给,不能从左往右给,也不能跳跃给缺省值,如下所示:

//半缺省函数
void Func(int x, int y = 20, int z = 30)
{cout << "x = " << x << "\ty = " << y << "\tz = " << z << endl;
}void Func(int x, int y, int z = 30)
{cout << "x = " << x << "\ty = " << y << "\tz = " << z << endl;
}

为什么必须要从右往左给形参缺省值呢?为什么不能从左往右给缺省值呢?具体原因如下所示:

void Func(int x = 10, int y = 20, int z)
{cout << "x = " << x << "\ty = " << y << "\tz = " << z << endl;
}int main()
{Func(1, 2);return 0;
}

当如上图所示传实参时:调用Func函数时,会产生歧义,不知道实参1和实参2的值是传给哪个形参,很明显实参2传递给形参 z 的,而实参1的值是传递哪个形参呢?是 x 还是 y ,这是不明确的,而从右往左给缺省值就不会存在这样的歧义:

void Func(int x, int y = 20, int z = 30)
{cout << "x = " << x << "\ty = " << y << "\tz = " << z << endl;
}int main()
{Func(1, 2);return 0;
}

第一个实参一定是传给第一个形参,第二个实参一定是传给第二个形参,不会存在什么歧义问题。若是传一个实参,那么这个实参一定是传给函数参数列表中的第一个形参;若是传两个实参,那么这两个实参一定是传给函数的参数列表中的前两个形参。

当函数为半缺省函数时,没有缺省值的形参必须要有与之相对应的实参,也就是说,至少要传一个实参。

函数的声明与定义分离时,缺省参数不能在函数声明和定义中同时出现,规定必须函数声明给缺省值,缺省值不仅仅只能是具体的值,还可以是表达式。如下所示:

声明:

在这里插入图片描述

定义:

在这里插入图片描述

2. 函数重载

什么是函数重载?函数重载指的是:一个函数名可以有多重的意义。C++中支持在同一作用域中出现同名函数,但是要求这些同名函数的形参不同,而C语言不支持。

int Add(int x, int y)
{return x + y;
}double Add(double x, double y)
{return x + y;
}short Add(short x, short y)
{return x + y;
}//……………………

这里的参数不同可以是参数的数据类型不同,参数的个数不同,参数的顺序不同,如下所示:

//以下两个 Add 函数构成函数重载 —— 参数类型不同
int Add(int x, int y)
{return x + y;
}double Add(double x, double y)
{return x + y;
}//以下两个 Add 函数构成函数重载 —— 参数个数不同
short Add(short x, short y)
{return x + y;
}short Add(short x, short y, short z)
{return x + y + z;
}//以下两个 Add 函数构成函数重载 —— 参数顺序不同
long Add(int x, long y)
{return x + y;
}int Add(long x, int y)
{return x + y;
}

但是有时构成重载的两个函数在被调用时会产生歧义,如下代码所示:

//以下两个 Func 函数构成函数重载 —— 参数的个数不同
void Func()
{cout << "Func( )" << endl;
}void Func(int n = 4)
{cout << "Func( int n )" << endl;
}

若是传参调用 Func 函数,调用的一定是第二个 Func 函数;若是不传参调用,那会调用哪个函数?其实这两个函数都会调用,因为无参的函数既可以调用本就是无参的函数,也可以调用全缺省函数。这时代码就会报错,解决方法是不要将这两个函数同时写,用第二个函数去替代。

注意:函数的返回值类型不同不能作为函数重载的条件,因为返回值可以不被接收。

3. 引用

3.1 引用的基础知识

引用的概念:引用不是新定义一个变量,而是给已存在的变量取了一个别名。引用的定义:类型& 引用别名 = 引用对象。如下所示:

// ra 就是 a 的别名
int a = 10;
int& ra = a;

编译器不会为引用变量开辟内存空间,引用变量和引用对象共用同一块内存空间:

在这里插入图片描述
由此图就可以得知引用变量与引用对象指向的是同一块内存空间,ra 和 a 的地址都是一样的,且变量的值也是一样的。

对引用变量操作还会改变其引用对象,如下所示:

在这里插入图片描述
ra 和 a 的值都被改变了。

此外也可以对同一个变量取多个别名,也可以对别名取别名,如下所示:

在这里插入图片描述
它们的值是一样的,地址也是一样的。

在引用中尤其要注意以下的代码:

int x = 20;
int& rx = x;
int y = 30;
rx = y;

请问这里的 rx = y 是在给 y 取别名吗?而 y 的别名是 rx 吗?其实并不是,这里只是将 y 的值赋值给了 rx ,修改了 rx 和 x 的值,来看一下调试窗口:

在这里插入图片描述
这里就说明了引用不会改变指向,只要它是一个变量的引用,那么它永远都是那个变量的别名,没有语法能改变引用的指向C++为了区分引用与取地址,只有&跟在类型的后面才称之为引用,单独一个&是取地址操作符,对变量执行取地址操作

根据上述的分析,总结出引用的特性:

1. 引用在定义时必须初始化
2. 一个变量可以有多个引用
3. 引用一旦引用一个实体,就不能再引用其它实体了

3.2 引用的作用

在C语言中,若想要交换两个变量的值,需要将这两个变量的地址传给形参,如此才能完成两个变量值的交换;但是在C++中,可以使用引用,如下所示:

// rx 是 x 的别名,ry 是 y 的别名
void Swap(int& rx, int& ry)
{int tmp = rx;rx = ry;ry = tmp;
}int main()
{int x = 10;int y = 20;cout << "交换前:x = " << x << "\ty = " << y << endl;Swap(x, y);cout << "交换后:x = " << x << "\ty = " << y << endl;return 0;
}

运行结果:

在这里插入图片描述

由此可以得出引用的功能:做函数的参数,修改形参影响实参

在C语言中,使用传值传参时,若实参的内存很大,那么往往会改为传址传参,因为这里会涉及拷贝问题;但是在C++中,可以使用引用,如下所示:

struct Test
{int arr[100000];int size = 0;int capacity = 0;
};// rtst 是 tst 的别名
void Func(struct Test& rtst)
{}int main()
{Test tst;Func(tst);return 0;
}

由此可以得出引用的功能:做函数的参数,减少拷贝,提高效率

既然可以给整型变量取别名,那可不可以给指针变量取别名呢?当然可以。在C语言中,若想交换两个指针的内容,可以将指针的地址传给形参,也就是使用二级指针;在C++中,可以使用引用来解决这个问题:

void Swap(int*& rpx, int*& rpy)
{int* tmp = rpx;rpx = rpy;rpy = tmp;
}int main()
{int x = 10;int y = 20;int* px = &x;int* py = &y;cout << "交换前:px = " << px << "\tpy = " << py << endl;Swap(px, py);cout << "交换后:px = " << px << "\tpy = " << py << endl;return 0;
}

运行结果:

在这里插入图片描述
若想要修改函数的返回值该怎么做呢?可以使用传引用返回,那么为什么不能使用传值返回呢?首先得要理解一个概念,只要是传值返回,返回的都不是原来的值,都会拷贝生成一个临时变量,返回的是这个临时变量,下面来介绍具体原因:

在这里插入图片描述

由此可以得出引用功能:引用作为函数的返回值,如此就可以修改返回对象;当函数的返回值的内存较大时,用引用作为函数的返回值,可以减少拷贝,提高效率

虽然引用非常的强大,但是并不是所有的函数都要用引用作为返回值,若所有的函数的返回值都用引用,那么会造成意想不到的后果,如下代码所示:

在这里插入图片描述

跟C语言中学的野指针一样,这里类似于野指针,由于是引用,可以称之为野引用
当返回的对象出作用域后未被销毁,那么可以使用传引用返回;若返回的对象出作用域后被销毁了,则不可以使用传引用返回,应该使用传值返回

从函数栈帧的角度去考虑:

在这里插入图片描述
所以并不是所有的函数都要用引用作为返回值,当函数变量为局部变量的时候,出了作用域该变量就销毁了,此时返回该局部变量的别名是一个危险的行为。

3.3 const 引用

1. 若想要引用一个const 对象,那么就必须要用使用 const 引用

int main()
{int x = 10;int& rx = x;const int y = 20;const int& ry = y;return 0;
}

若这里引用 const 修饰的对象时,并没有使用 const 引用,那么程序会报错,这是因为这里涉及到了权限的放大 —— 原本变量 y 被 const 修饰后不能被修改,现在对 y 取别名后,就可以通过修改别名来修改变量 y ,但是 y 本身是不能被修改的,别名却可以修改 y ,这样是不可取的。权限是不能放大的。const 引用也可以引用普通对象,因为普通对象的访问权限在引用过程中可以缩小,但是不能放大。如下所示:

int main()
{int x = 10;int& rx = x;const int& r = x;int& const p = x;return 0;
}

这里就涉及到了权限的缩小,之前 x 的值是可以修改的,但是现在被 const 引用修饰后,x 的值就不可以修改了,这就是权限的缩小。这里 const 写在类型的左右边都可以,但是一般写在类型的左边。

接下来根据以下代码来回答下面的问题:

int a = 1;
int& ra = a;
int b = a;

该代码涉及权限的放大或缩小吗?都不涉及,只有指针和引用才涉及权限的放大与缩小,这里只是将变量 a 的值赋值给变量 b 。

2. 要注意的是类似int& rb = a * 4;double c = 3.14;int& rc = c;这样一些场景下a * 4的结果保存在一个临时对象中;int& rc = c也是类似的情况,在类型转换时会将中间值用临时对象来保存。也就是说,rb与rc引用的都是临时对象,而C++规定临时对象具有常性。所以在这里就涉及到了权限的放大,必须要用const引用才行

int main()
{int a = 10;int b = 20;double c = 3.14;const int& r1 = a;          //给变量取别名const int& r2 = b;          //给变量取别名const int& r3 = 30;         //给常量取别名 //将double类型的变量c的整型部分给给临时对象const int& r4 = c;          //当表达式运算完毕后会计算出一个结果,这个结果存储在临时对象中//r5引用的是表达式运算的结果,也就是临时对象const int& r5 = a * 4;  return 0;
}

之后在写函数的参数的时候,就使用引用来作为形参,视情况考虑是否加const。当函数的参数使用const引用后,参数就可以是临时对象了,使用 const 引用的好处是既可以接收普通变量,也可以接收 const 修饰的变量。如下代码所示:

int main()
{int a = 10;int b = 20;double c = 3.14;const int& r1 = a;         const int& r2 = b;                         Func(r1);Func(r2);Func(10);Func(c);Func(a * 4);return 0;
}

3.4 指针与引用的关系

引用可以完全替代指针吗?当然是不可以的。指针有一个引用做不到的事情,就是改变指向。在数据结构中是时常要改变指向的,在这方面引用就无法做到了,必须使用引用。在实践中指针与引用相辅相承,功能有重叠性,但是各有各的特点,互相是不可替代的:

1. 语法概念上,引用是对一个对象的取别名,不会开辟空间;指针是存储一个对象的地址,需要开辟空间
2. 引用在定义时必须要初始化,指针可以初始化也可以不初始化,但是建议初始
3. 引用在初始化时引用一个对象后,就不能再引用其它对象了,而指针可以不断的改变其指向的对象
4. 引用可以直接访问其指向的对象,而指针需要解引用后,访问的才是其指向的对象
5. 指针很容易出现空指针和野指针问题,引用很少出现,引用使用起来更加的安全
6. sizeof的含义不同,引用结果为引用类型的大小,但是指针始终是地址空间的所占字节个数(32位机器平台下是4个字节,64位机器平台下是8个字节)

在这里插入图片描述

底层指针与引用之间的区别:

在这里插入图片描述
从指令汇编角度来看,引用是由指针实现的

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

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

相关文章

Rust 官方文档:人话版翻译指南

鉴于大部分翻译文档都不太会说人话&#xff0c;本专栏主要内容为 rust 程序设计语言、rust 参考手册、std 库 等官方文档的中译中。

FlySecAgent:——MCP全自动AI Agent的实战利器

最近&#xff0c;出于对人工智能在网络安全领域应用潜力的浓厚兴趣&#xff0c;我利用闲暇时间进行了深入研究&#xff0c;并成功开发了一款小型轻量化的AI Agent安全客户端FlySecAgent。 什么是 FlySecAgent&#xff1f; 这是一个基于大语言模型和MCP&#xff08;Model-Contr…

实战项目5(08)

目录 任务场景一 【r1配置】 【r2配置】 【r3配置】 ​​​​​​​任务场景二 【r1配置】 【r2配置】 ​​​​​​​任务场景一 按照下图完成网络拓扑搭建和配置 任务要求&#xff1a; 通过在路由器R1、R2和R3上配置静态路由&#xff0c;实现网络中各终端PC能够正常…

基于Kubernetes的Apache Pulsar云原生架构解析与集群部署指南(下)

文章目录 k8s安装部署Pulsar集群前期准备版本要求 安装 Pulsar Helm chart管理pulsarClustersBrokersTopic k8s安装部署Pulsar集群 前期准备 版本要求 Kubernetes 集群&#xff0c;版本 1.14 或更高版本Helm v3&#xff08;3.0.2 或更高版本&#xff09;数据持久化&#xff…

C35-数组和函数开发初见

一 数组作为函数的参数 用于传递数组中的某一个元素→意义不大 数组名当做函数实际参数 示例 代码 #include <stdio.h>//封装函数PrintArr void PrintArr(int arr[3]){int i;for(i0;i<3;i){printf("%d ",arr[i]);}putchar(\n);}//主函数 int main() { …

【小沐学GIS】基于C++绘制二维瓦片地图2D Map(QT、OpenGL、GIS)

&#x1f37a;三维数字地球系列相关文章如下&#x1f37a;&#xff1a;1【小沐学GIS】基于C绘制三维数字地球Earth&#xff08;OpenGL、glfw、glut&#xff09;第一期2【小沐学GIS】基于C绘制三维数字地球Earth&#xff08;OpenGL、glfw、glut、GIS&#xff09;第二期3【小沐学…

idea左侧项目资源管理器不见了处理

使用idea误触导致&#xff0c;侧边栏和功能栏没了&#xff0c;如何打开&#xff1f; 1.打开文件&#xff08;File&#xff09; 2. 打开设置&#xff08;Settings&#xff09; 3.选择Appearance&Behavior--->Appearance划到最下面&#xff0c;开启显示工具栏和左侧并排布…

[Java实战]Spring Boot 静态资源配置(十三)

[Java实战]Spring Boot 静态资源配置&#xff08;十三&#xff09; 引言 静态资源&#xff08;如 HTML、CSS、JavaScript、图片等&#xff09;是 Web 应用的基石。Spring Boot 通过自动化配置简化了静态资源管理&#xff0c;但面对复杂场景&#xff08;如多模块项目、CDN 集成…

多模态大语言模型arxiv论文略读(六十九)

Prompt-Aware Adapter: Towards Learning Adaptive Visual Tokens for Multimodal Large Language Models ➡️ 论文标题&#xff1a;Prompt-Aware Adapter: Towards Learning Adaptive Visual Tokens for Multimodal Large Language Models ➡️ 论文作者&#xff1a;Yue Zha…

Python 基础语法与数据类型(七) - 函数的定义与调用 (def, return)

文章目录 为什么要使用函数&#xff1f;函数的定义 (def)函数的调用函数参数 (Parameters vs Arguments)返回值 (return)变量作用域 (简要了解)总结练习题练习题答案 **创作不易&#xff0c;请大家点赞加收藏&#xff0c;关注我&#xff0c;持续更新教程&#xff01;** 到目前为…

华为配置篇-RSTP/MSTP实验

MSTP 一、简介二、常用命令总结三、实验 一、简介 RSTP&#xff08;快速生成树协议&#xff09;​ RSTP&#xff08;Rapid Spanning Tree Protocol&#xff09;是 STP 的改进版本&#xff0c;基于 ​​IEEE 802.1w 标准​​&#xff0c;核心目标是解决传统 STP 收敛速度慢的问…

Docker Compose 完全指南:从入门到生产实践

Docker Compose 完全指南&#xff1a;从入门到生产实践 1. Docker Compose 简介与核心价值 Docker Compose 是一个用于定义和运行多容器 Docker 应用程序的工具。通过一个 YAML 文件来配置应用的服务&#xff0c;只需简单命令就能创建和启动所有服务。 核心优势&#xff1a;…

Linux 离线安装 Docker 和 Docker Compose 最新版 的完整指南

一、准备工作 1. 下载安装包​&#xff08;需在有网络的机器操作&#xff09;&#xff1a; Docker 引擎&#xff1a;从官方仓库下载最新二进制包 wget https://download.docker.com/linux/static/stable/x86_64/docker-24.0.6.tgz​Docker Compose&#xff1a;下载最新二进制…

CSS: 选择器与三大特性

标签选择器 标签选择器就是选择一些HTML的不同标签&#xff0c;由于它们的标签需求不同&#xff0c;所以CSS需要设置标签去选择它们&#xff0c;为满足它们的需求给予对应的属性 基础选择器 标签选择器 <!DOCTYPE html> <head><title>HOME</title>…

鸿蒙跨平台开发教程之Uniapp布局基础

前两天的文章内容对uniapp开发鸿蒙应用做了一些详细的介绍&#xff0c;包括配置开发环境和项目结构目录解读&#xff0c;今天我们正式开始写代码。 入门新的开发语言往往从Hello World开始&#xff0c;Uniapp的初始化项目中已经写好了一个简单的demo&#xff0c;这里就不再赘述…

JavaSE核心知识点02面向对象编程02-08(异常处理)

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 JavaSE核心知识点02面向对象编程02-08&#…

【JVM-GC调优】

一、预备知识 掌握GC相关的VM参数&#xff0c;会基本的空间调整掌握相关工具明白一点&#xff1a;调优跟应用、环境有关&#xff0c;没有放之四海而皆准的法则 二、调优领域 内存锁竞争cpu占用io 三、确定目标 【低延迟】&#xff1a;CMS、G1&#xff08;低延迟、高吞吐&a…

基于单片机的电子法频率计

一、电子计数法测频率原理 通过门控控制闸门开关&#xff0c;闸门时间T自己设定&#xff0c;计数器计数脉冲个数N&#xff08;也就是待测信号&#xff09;&#xff0c;N个脉冲的时间间隔为δt,倒数即为信号的频率f,由此 δtT/N fN/T——信号频率 根据公式&#xff0c;如果考虑…

【C/C++】跟我一起学_C++同步机制效率对比与优化策略

文章目录 C同步机制效率对比与优化策略1 效率对比2 核心同步机制详解与适用场景3 性能优化建议4 场景对比表5 总结 C同步机制效率对比与优化策略 多线程编程中&#xff0c;同步机制的选择直接影响程序性能与资源利用率。 主流同步方式: 互斥锁原子操作读写锁条件变量无锁数据…

判断两台设备是否在同一局域网内的具体方法

以下是判断两台设备是否在同一局域网内的具体方法&#xff1a; 1. 检查IP地址和子网掩码 操作步骤&#xff1a; Windows系统&#xff1a; 按 Win R 键&#xff0c;输入 cmd 并回车。输入 ipconfig&#xff0c;查看 IPv4 地址 和 子网掩码&#xff08;如 192.168.1.5/255.255.2…