C++基础——对于C语言缺点的补充(2)

       上篇文章中说到,为了解决C语言会出现人为定义的函数和库函数出现重定义的错误,C++引入了一个新的概念,即命名空间,通过认为定义命名空间,来解决上述问题。

       在本篇文章中,将继续介绍C++相对于C语言不足来进行的补充,例如:缺省参数,重载函数等等。

1. 缺省参数:

1.1 缺省参数的定义以及单个缺省参数简单应用:

       缺省参数的定义大致如下:缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。例如下面的函数:

void PRINT(int a = 0)
{cout << a << endl;
}int main()
{PRINT(0);PRINT(10);return 0;
}

       不难发现,相比C语言中的函数书写方法,C++中,对于函数的形式参数给定了一个初始值0,该值就是上面定义中所说的缺省值。对于这种带有缺省值的函数,其调用结果如下:

       这也证实了定义中的一句话,如果没有对函数的实参进行指定赋值,则在函数执行时默认采用形参,反之采用实参。

1.2 多个缺省参数的应用:

       在上面的情况中,函数只有单一的缺省值。当函数的缺省值数量大于1时,函数的调用方式会更加多样,例如:

void PRINT(int a = 10, int b = 20, int c = 30)
{cout << a << endl;cout << b << endl;cout << c << endl;
}int main()
{PRINT();PRINT(1);PRINT(1, 2);PRINT(1, 2, 3);return 0;
}

 运行结果为:

       通过上面的例子可以看到,针对这种缺省值数量大于1的情况下,函数的调用会因为实参的给定而给出不用的效果。

       但是对于实参数值的给定,需要注意,实参的给定必须是连续的,中间不能有跳跃的情况,例如:

PRINT(, 2, );

1.3 全缺省与半缺省:

      全缺省参数就是上一部分的代码所展示,即所有的形参都给定了缺省值。对于半缺省,则是对于形式参数,不全部给定缺省值,例如:

void PRINT(int a, int b = 20, int c = 30)

但是对于半缺省参数的给定需要注意以下两点:

1. 缺省值必须从右向左给定,且必须是连续的。

2. 当函数的定义和声明分离时,缺省参数不能同时在声明和定义中出现。一般是将缺省参数给定到函数声明中

2. 函数重载:

2.1 函数重载的定义及类型:

       函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。例如下面给出的例子:

int ADD(int num1, int num2)
{cout << "int ADD(int num1, int num2) = ";return num1 + num2;
}double ADD(double num1, double num2)
{cout << " double ADD(double num1, double num2) = ";return num1 + num2;
}
int main()
{cout << ADD(1, 2) << endl;cout << ADD(1.1, 2.2) << endl;
}

       在上述代码中,建立两个函数,且这两个函数函数名相同,都是ADD,但是这两个函数的返回值类型,以及参数类型都不同,此时符合上方函数重载的定义,构成了函数重载。

      对于上述代码,运行结果如下:

         

       上面的例子展示了函数的类型不同,以及其参数类型不同的情况下可以构成重载。当函数的参数数量不同时,即:

void fun()
{cout << "fun()" << endl;
}void fun(int a)
{cout << "fun(a)" << endl;
}
int main()
{fun();fun(1);
}

      此时符合重载定义中参数数量不同这一特性,程序依旧可以正常运行,运行结果如下:

    但是上述函数有一种特殊情况需要注意,即:将函数的参数给定一个缺省值,例如:

void fun()
{cout << "fun()" << endl;
}void fun(int a = 0)
{cout << "fun(a)" << endl;
}
int main()
{fun();fun(1);
}

      运行程序,编译器会报错。这是因为在存在一个不含参数的函数与另一个有参数,且参数有缺省值的情况下,同时运行两个函数会造成二义性,即:编译器不能判断在main函数中,调用函数时,调用的函数是上面两个函数哪一个函数。

    对于函数重载,还有一种方式,即类型的顺序不同,具体情况如下方代码所示:

void fun(int a, char b)
{cout << "void fun(int a, char b)" << endl;
}void fun(char a, int b)
{cout << "void fun(char a, int b)" << endl;
}
int main()
{fun(2, 1.1);fun(2.2, 1);return 0;
}

3. 引用:

3.1 引用的概念以及基本使用:

       在C++中,引用是基于C语言中指针的不足,为解决这些不足创建的。引用的概念如下:引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。

      对应代码如下:

int main()
{int a = 1;int& c = a;
}

     main函数中,第一行代码创建了一个名为a的整型变量,第二行则是引用,即给已有的整型变量a取了一个别名叫做c

     在上面的概念中提到,编译器不会为引用变量开辟内存空间,引用与其引用的变量共用一块内存空间。因此,加入对引用进行更改,相应的变量也会更改,例如对于下面代码:

int main()
{int a = 1;int& c = a;cout << "更改前 a=" << a << endl;cout << "更改前 c=" << c << endl;c++;cout << "更改后 a=" << a << endl;cout << "更改后 c=" << c << endl;return 0;
}

运行结果为:

引用不光可以用在已有的变量上,也可以对引用进行引用,即:

int main()
{int a = 1;int& c = a;int& e = c;cout << "a = " << a << endl;cout << "c=" << c << endl;cout << "e=" << e << endl;return 0;
}

 运行结果为:

        对于引用而言,其最大的价值适用于做参数,例如在之前文章中提到到交换函数:

void Swap(int* a, int* b)
{int tmp = *a;*a = *b;*b = tmp;
}
int main()
{int x = 5;int y = 1;Swap(&x, &y);printf("%d %d", x, y);return 0;
}

        由于函数形参只是实参的一份临时拷贝,形参的改变并不会影响实参,所以在传递参数时,需要传递变量的地址。

        如果利用引用的特性,即:引用的改变会引起变量的改变,来实现交换函数,则可以一定程度上化简上述函数,即:
 

void Swap(int& a, int& b)
{int tmp = a;a = b;b = tmp;
}
int main()
{int x = 5;int y = 1;Swap(x, y);printf("%d %d", x, y);return 0;
}

      运行结果如下:

3.1 使用引用的注意事项: 

3.1.1 引用与初始化:

       在指针这部分中,如果一个指针只是被创建但是不被初始化,则这个指针称为野指针。不过指针的初始化并不是必然的,但是对于引用来说,必须被初始化,例如:

int main()
{int a = 0;int& b;return 0;
}

由于引用没有被初始化,因此程序错误。

3.1.2 引用能不能被改变指向:

        在数据结构关于的链表的文章中,如果需要在链表中插入一个新的结点,需要改变链表中指针的指向,但是对于引用而言,不能被改变指向,例如:

int main()
{int a = 0;int& b = a;int c = 1;printf("%p %p %p", &a, &b, &c);printf("\n");b = c;cout << "a = " << a << "b = " << b << "c = " << c << endl;printf("%p %p %p", &a, &b, &c);return 0;
}

运行结果如下:


       通过结果不难发现,在执行了b=c这一条代码后,b的地址依旧与a的地址相同,说明没有改变其指向。只是改变了a,b的值。

      因此,引用虽然在一定程度上弥补了指针的不足,但是由于引用并不能改变指向,因此引用并不能完全替代指针。

3.2 引用返回:

3.2.1 引用返回的概念:

在解释本节标题的内容之前,首先需要了解一个概念:

int fun()
{int n = 0;n++;return n;
}
int main()
{int ret = fun();return 0;
}

       图中给定的程序的函数的返回值返回n,在函数外部创建了一个名为ret的变量来接收函数返回值,但是,由于在函数fun运行结束后,该函数对应的栈帧全部销毁,因此,ret并不是直接接收函数的返回值,而是返回值先被存到寄存器中,ret通过寄存器来间接接收这个值。

      此时,如果将函数返回的返回值改为下方代码所示,即:

int& fun()
{int n = 0;n++;return n;
}
int main()
{int ret = fun();return 0;
}

此时再去运行程序,编译器会给出警告。这是因为在这种返回类型下,返回值并不能确定,如果编译器在函数结束后直接清理掉栈帧,则返回空,如果编译器在函数结束后,并不会立即清理掉栈帧,则返回还能有返回值。

(注:由于VS并不会立刻清理掉栈帧,因此运行该程序,返回值会返回1

       通过上面的例子,可以得出一个结论,即:在函数运行结束后,即离开函数的作用域后,此时的返回对象被销毁,因此不能采用引用返回。

但是,对于静态变量和利用malloc类似方式创建的变量,在函数结束后不会销毁,可以采用引用返回。

3.2.2 引用返回的价值:

       由于用返回的价值主要体现在C++的后续内容,因此,本文在此部分指给出结论,原理会在后续的文章中进行阐述。

1. 提高返回速度  

2. 可以修改返回对象

3.3 常引用:

对于常引用,具体可以有下面代码反应:

int main()
{const int a = 1;int& b = a;return 0;
}

        从上面代码可以看到,变量a是一个被const修饰的常变量,下面对变量a进行了一次引用。当去运行上述代码时,编译器会显示错误。

      但是如果将上述代码进行修改,即:

int main()
{const int a = 1;const int& b = a;return 0;
}

       运行此代码时,并没有错误,不难观察到,上下的给的代码唯一的差距就是第二次给出的代码的引用前加了const。因此,在进行常引用的过程中,一定不能涉及变量权限的放大。例如上面运行失败的代码中,原本变量a的权限是不能修改,但是在引用后,由于引用的修改会引起原变量的修改,因此,引用的权限超过了原变量的权限,造成了权限放大。所以导致运行错误。这里需要注意的时,只是不能放大变量的权限,对于变量权限的平移的缩小均无影响,例如:
 

int main()
{int c = 2;const int& d = c;return 0;
}

      上述代码在引用后,缩小了变量的权限,可以正常运行。

      另外,对于一个常量进行常饮用也是允许的,即:

const int& e = 10;

令外,当引用与被引用的对象存在类型转换的情况时,即:

int j = 10;double h = 15;const double& k = j;

由于类型转化的过程中,会额外生成一个常量作用于该过程,引用时k接收的值并不是i,而是i通过一个常量完成类型转换间接传给k,由于常量的常性,因此需要加上const

4. 指针与引用的练习与区别:

     1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
     2. 引用在定义时必须初始化,指针没有要求
     3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何
一个同类型实体
    4. 没有NULL引用,但有NULL指针
    5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32
位平台下占4个字节)
   6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
   7. 有多级指针,但是没有多级引用
   8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
   9. 引用比指针使用起来相对更安全

  10.在引用这一小节开头的时候说到,引用是针对一个对象起的别名,与被引用对象共用一块空间。在汇编层上,引用是利用指针来实现的,因此也需要开辟空间。不过,在日常运用时,一般认为引用是不会开辟新空间的。

5. 内联函数:

       对于一般的函数,在函数作用时需要开辟栈帧,而内联函数不需要开辟栈帧,而是在运行中原地展开。由于内联函数不需要开辟栈帧,因此提高了程序的运行效率。

      内敛函数的书写,只需要在函数声明的开头加上inline即可,例如:

inline int Add(int x, int y)
{int z = x + y;return x + y;
}
int main()
{int x = 5;int y = 1;int ret = Add(x, y);return 0;
}

            但是需要注意,内联函数并不能完全替代普通函数,并且,当一个函数的代码函数>10行时,不建议采用内联函数。这是因为,内联函数每次调用时都会原地展开,当调用的次数较大时,例如调用10000次,则一个代码行数为50行的内联函数完全展开后为500000行,延缓程序的运行速度。

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

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

相关文章

Node.js、Chrome V8 引擎、非阻塞式I/O介绍

目录 Node.js介绍Chrome V8 引擎介绍非阻塞式I/O介绍 &#x1f44d; 点赞&#xff0c;你的认可是我创作的动力&#xff01; ⭐️ 收藏&#xff0c;你的青睐是我努力的方向&#xff01; ✏️ 评论&#xff0c;你的意见是我进步的财富&#xff01; Node.js介绍 Node.js 是一个…

Harbor企业级Registry基础镜像仓库的详细安装使用教程(保姆级)

Harbor Docker 官方提供的私有仓库 registry&#xff0c;用起来虽然简单 &#xff0c;但在管理的功能上存在不足。 Harbor是vmware一个用于存储和分发Docker镜像的企业级Registry服务器&#xff0c;harbor使用的是官方的docker registry(v2命名是distribution)服务去完成。 ha…

数据结构之堆的实现(图解➕源代码)

一、堆的定义 首先明确堆是一种特殊的完全二叉树&#xff0c;分为大根堆和小根堆&#xff0c;接下来我们就分别介绍一下这两种不同的堆。 1.1 大根堆&#xff08;简称&#xff1a;大堆&#xff09; 在大堆里面&#xff1a;父节点的值 ≥ 孩子节点的值 我们的兄弟节点没有限制&…

pandas 笔记:get_dummies分类变量one-hot化

1 函数介绍 pandas.get_dummies 是 pandas 库中的一个函数&#xff0c;它用于将分类变量转换为哑变量/指示变量。所谓的哑变量&#xff0c;就是将分类变量的每一个不同的值转换为一个新的0/1变量。在输出的DataFrame中&#xff0c;每一列都以该值的名称命名 pandas.get_dummi…

java使用bouncycastle加解密

jdk默认带了一些常见的加解密方式&#xff0c;当我们常见的加解密不能满足时&#xff0c;就需要用到一些第三方的库了&#xff0c;bouncycastle就是其中一种。 但是bouncycastle文档比较少。简单介绍一下写法 1.导入依赖 <dependency><groupId>org.bouncycastle&…

5 ip的分配

如上一节所述&#xff0c;需要和其他设备通信&#xff0c;那么需要先配置ip. 1、如何配置ip 1.可以使用 ifconfig&#xff0c;也可以使用 ip addr 2.设置好了以后&#xff0c;用这两个命令&#xff0c;将网卡 up 一下&#xff0c;就可以了 //---------------------------- 使…

AI大模型时代网络安全攻防对抗升级,瑞数信息变革“下一代应用与数据安全”

AI与大模型技术加速普及&#xff0c;安全领域也在以创新视角聚焦下一代应用安全WAAP变革&#xff0c;拓展新一代数据安全领域。近日瑞数信息重磅发布了瑞数全新API扫描器、API安全审计、数据安全检测与应急响应系统及分布式数据库备份系统四大新品。此次发布在延续瑞数信息Bot自…

用java代码实现QQ第三方登录

QQ第三方登录需要使用到QQ互联开放平台提供的API&#xff0c;在Java中可以使用OAuth2.0协议来实现第三方登录。 具体实现步骤如下&#xff1a; 注册QQ互联开放平台账号&#xff0c;并创建应用&#xff0c;获取到App ID和App Secret。 在Java项目中导入QQ互联开放平台提供的Ja…

当zmq 和 docker 都要绑定一个端口时,怎么不修改端口号就能解决冲突?

问题描述 docker容器中的程序需要和外部进行通讯&#xff0c;但是当作为请求方向 响应方发送数据时&#xff0c;外部的进程因为需要绑定的端口被docker占用而绑定失败。 解决方式 方式一&#xff1a;使用请求响应方式&#xff0c;但是将响应端放置到容器内部。 方拾二&#…

freertos简单串口

先来完善一下FreeRTOSConfig.h这个配置文件 /*FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd.All rights reservedVISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.This file is part of the FreeRTOS distribution.FreeRTOS is …

vue3+ts+vite搭建项目

检查node版本 > node -v检查vue版本 > vue -V1、全局安装Vue CLI 3.X版本 > npm install -g vue/cli2、创建vue 项目 > vue create [project-name]3、安装vite > npm install vite4、使用vite Vite 需要 Node.js 版本 > 12.0.0。 > npm init vitela…

通信原理板块——时域均衡

微信公众号上线&#xff0c;搜索公众号小灰灰的FPGA,关注可获取相关源码&#xff0c;定期更新有关FPGA的项目以及开源项目源码&#xff0c;包括但不限于各类检测芯片驱动、低速接口驱动、高速接口驱动、数据信号处理、图像处理以及AXI总线等 1、均衡器 为了减小码间串扰的影响…

放卷开环张力控制(伺服转矩模式应用)

收放卷张力开环闭环控制算法,请参考下面文章链接: PLC张力控制(开环闭环算法分析)_RXXW_Dor的博客-CSDN博客文章浏览阅读4k次,点赞3次,收藏3次。里工业控制张力控制无处不在,也衍生出很多张力控制专用控制器,磁粉制动器等,本篇博客主要讨论PLC的张力控制相关应用和算…

Scala和Play WS库编写的爬虫程序

使用Scala和Play WS库编写的爬虫程序&#xff0c;该程序将爬取网页内容&#xff1a; import play.api.libs.ws._ import scala.concurrent.ExecutionContext.Implicits.global ​ object BaiduCrawler {def main(args: Array[String]): Unit {val url ""val proxy…

CMake教程-第 12 步:打包调试和发布

CMake教程-第 12 步&#xff1a;打包调试和发布 1 CMake教程介绍2 学习步骤[Step 1: A Basic Starting Point](https://blog.csdn.net/u014100559/article/details/133099915?spm1001.2014.3001.5501)[Step 2: Adding a Library](https://blog.csdn.net/u014100559/article/de…

【学习笔记】[COCI2018-2019#1] Teoretičar

首先&#xff0c;可以发现 C C C等于所有点度数的最大值&#xff0c;我们能用到的颜色数目为 2 x ≥ C 2^x\ge C 2x≥C。 考虑分治&#xff0c;将边集划分为 E E 1 E 2 EE_1E_2 EE1​E2​&#xff0c;使得 E 1 , E 2 E_1,E_2 E1​,E2​中点度数的最大值都不超过 2 x − 1 2^…

06.Oracle数据备份与恢复

Oracle数据备份与恢复 一、通过RMAN方式备份二、使用emp/imp和expdb/impdb工具进行备份和恢复三、使用Data guard进行备份与恢复 一、通过RMAN方式备份 通过 RMAN&#xff08;Oracle 数据库备份和恢复管理器&#xff09;方式备份 Oracle 数据库&#xff0c;可以使用以下步骤&a…

网速和带宽浅析

经常会对交换机的带宽和文件的存储的单位混淆不清&#xff0c;下面进行整理分析。 1、网速 网速是通俗的叫法&#xff0c;专业的叫法是带宽。如手机上显示的网速为2.4K/s。 带宽通常有十兆、百兆、千兆&#xff0c;即10Mbps、100Mbps、1000Mbps&#xff0c;单位为bps。 2、带…

【微服务】mysql + elasticsearch数据双写设计与实现

目录 一、前言 二、为什么使用mysqles双写 2.1 单用mysql的问题 2.2 为什么不直接使用es 2.2.1 非关系型表达 2.2.2 不支持事务 2.2.3 多字段将造成性能低下 三、mysqles双写方案设计要点 3.1 全新设计 VS 中途调整架构 3.2 全表映射 VS 关键字段存储 3.2.1 最大程度…

FPGA高端项目:图像采集+GTP+UDP架构,高速接口以太网视频传输,提供2套工程源码加QT上位机源码和技术支持

目录 1、前言免责声明本项目特点 2、相关方案推荐我这里已有的 GT 高速接口解决方案我这里已有的以太网方案 3、设计思路框架设计框图视频源选择OV5640摄像头配置及采集动态彩条视频数据组包GTP 全网最细解读GTP 基本结构GTP 发送和接收处理流程GTP 的参考时钟GTP 发送接口GTP …