C++基础入门(二)(函数重载,引用,内联函数,nullptr)

目录

一. 函数重载

1. 概念

2. 实现

(1). 参数类型不同

(2). 参数个数不同

(3). 参数类型顺序不同

3. 注意事项

(1). 返回值不能作为重载的条件

(2). 不能仅按函数返回类型重载

(3). 与缺省参数的问题

二. 引用

1. 概念和定义

2. 引用的特性

(1). 引用在定义时必须初始化

(2). 一个变量可以有多个引用

(3). 引用一旦引用一个实体,再不能引用别的实体

3. 引用的使用

4. 引用做函数返回值

5. const 引用

 6. 指针和引用的关系

三. 内联函数

概念

 使用

 注意

 四. nullptr


一. 函数重载

1. 概念

C++⽀持在同⼀作⽤域中出现同名函数,但是要求这些同名函数的形参不同,可以是参数个数不同或者类型不同。这样C++函数调⽤就表现出了多态⾏为,使⽤更灵活。C语⾔不⽀持同⼀作⽤域中出现同名函数的。

2. 实现

有三个使用情景,分别为参数类型不同,参数个数不同和参数类型顺序不同

(1). 参数类型不同
#include<iostream>using namespace std;
int Add(int aaaa, int bbbb)
{cout << "调用的是整数相加的函数" << endl;return aaaa + bbbb;
}double Add(double aaaa, double bbbb)
{cout << "调用的是双精度浮点数相加的函数" << endl;return aaaa + bbbb;
}int main()
{int a = 10, b = 22;double aa = 0.221, bb = 3.025;cout << a << " " << b << endl;cout << aa << " " << bb << endl;return 0;
}
(2). 参数个数不同
#include<iostream>using namespace std;
void n()
{cout << "调用的是无参数的函数" << endl;
}
void n(int a)
{cout << "调用的是有参数a的函数" << endl;
}int main()
{n();n(2);return 0;
}
(3). 参数类型顺序不同

#include<iostream>
using namespace std;
void nn(int a,char b)
{cout << "调用的是先int型数据再char型数据的函数" << endl;
}
void nn(char a,int b)
{cout << "调用的是先char型数据再int型数据的函数" << endl;
}
int main()
{nn(1, 'x');nn('x', 1);return 0;
}
3. 注意事项
(1). 返回值不能作为重载的条件

例如一下两个函数

int nn(int a,char b)
{return 1;
}int nn(int b,char a)
{return 0;
}

进行函数调用时并不能区分两个函数的差别,所以不可以

(2). 不能仅按函数返回类型重载

如下两个函数

void nn(int a,char b)
{printf("1111\n");
}int nn(int b,char a)
{return 0;
}
(3). 与缺省参数的问题
#include<iostream>using namespace std;void ffff()
{cout << "111111111" << endl;
}
void ffff(int a = 10)
{cout << a << endl;
}int main()
{	ffff(110);ffff();return 0;
}

以上两个代码构成重载

我们发现仅调用ffff(110);可以正常进行,但调用f();直接会报错,因为存在歧义,编译器会不知道调用谁

二. 引用

1. 概念和定义

引⽤不是新定义⼀个变量,⽽是给已存在变量取了⼀个别名,编译器不会为引⽤变量开辟内存空间, 它和它引⽤的变量共⽤同⼀块内存空间。就像人会有小名一样,例如张三,他的家人可能平时叫他小三,虽然称呼不同,但都是叫的张三这一个人

定义方法为:

类型& 引用别名 = 引用对象

C++中为了避免引入太多运算符,会复用C语言的一些符号,比如前面的<<和>>,这里引用和取地址使用了同一个符号,注意使用方法区分即可。

#include<iostream>
using namespace std;
int main()
{int aa = 0;//bb和cc是aa的别名int& bb = aa;int& cc = aa;//给别名bb取别名,dd仍然是aa的别名int& dd = bb;++dd;cout << aa << "  " << &aa << endl;cout << bb << "  " << &bb << endl;cout << cc << "  " << &cc << endl;cout << dd << "  " << &dd << endl;return 0;
}

假设a的别名是b,那b就等同于a

别名的别名d依然等同于其本身

2. 引用的特性
(1). 引用在定义时必须初始化

错误代码如下

#include<iostream>
using namespace std;
int main()
{int a = 111;int& aa;return 0;
}

 修改为

#include<iostream>
using namespace std;
int main()
{int a = 111;int& aa=a;return 0;
}
(2). 一个变量可以有多个引用

即一个人可以有多个称呼

#include<iostream>
using namespace std;
int main()
{int a = 111;int& aa=a;int& aaa = a;int& aaaa = a;return 0;
}
(3). 引用一旦引用一个实体,再不能引用别的实体

如下所示

#include<iostream>
using namespace std;
int main()
{int a = 111;int& aa=a;int c = 20;aa = c;cout << a << " " << aa << endl;return 0;
}

其中的 aa = c ; 并非是让aa称为c的别名,因为C++不能改变指向,这里是一个赋值操作

3. 引用的使用

在平时的实践中引用主要是在引用传参和引用做返回值中减少拷贝,提高效率。并且在改变引用对象时同时改变被引用对象

引用传参跟指针传参功能是类似的,引用传参相对更方便一些

代码如下

#include<iostream>
using namespace std;void Swap1(int* a, int* c)
{*a = 20;*c = 111;
}void Swap2(int& a, int& c)
{a = 20;c = 111;
}int main()
{int a1 = 111;int a2 = 111;int c1 = 20;int c2 = 20;Swap1(&a1, &c1);Swap2(a2, c2);cout << a1 << " " << c1 << endl;cout << a2 << " " << c2 << endl;return 0;
}

 我们发现两种方式均完成了改变

在之前数据结构的时间中我们都是传的指针但是也可以通过引用来替代

int StackSize(ST* ps)
{assert(ps);return ps->top;//栈的大小
}int StackSize(ST& ps)
{return ps.top;//栈的大小
}

 上述代码效果相同

指针变量也可以取别名

比如

void Push(pNode** pd,int x)
{//...........
}void Push(pNode*& pd,int x)
{//...........
}

这里的pNode*& pd就是给指针变量取别名,这样就不需要用二级指针了,相对而言简化了程序

4. 引用做函数返回值

不要返回局部变量引用

由于函数执行完毕时,局部变量的内存空间会被系统自动回收。如果此时外部代码试图通过引用访问该局部变量,实际上访问的是已经被回收的内存空间,被称为野引用或空引用,造成严重后果

返回值可以用引用接收

如果函数的返回值是引用那么这个函数调用可以做左值

#include<iostream>
using namespace std;int a = 100;
int& ppp()
{return a;
}
int main()
{int& c = ppp();ppp() = 10;cout << c << endl;return 0;
}

由于返回值是a的别名,所以可以直接通过赋值来改变他

5. const 引用

可以引用一个const对象,但是必须用const引用。const引用也可以引用普通对象,因为对象的访问权限在引用过程中可以缩小,但是不能放大。

#include<iostream>
#include"stack.h"
using namespace std;int main()
{const int aa = 10;//自身不能改变,别名自然也不能改变//int& d = aa;const int& c = aa;int b = 20;//权限可以缩小,不能放大const int& f = b;//int& e = f;//不可以增大权限//int& p = 30;const int& p = 30;return 0;
} 

const引用还可以引用常数

而在下列代码中

#include<iostream>
#include"stack.h"
using namespace std;int main()
{int a = 1;int b = 2;//int& t = (a + b);const int& t = (a + b);//引用的是临时对象,临时对象生命周期就跟着引用走//(临时对象在,两数加减乘除等,函数传值返回,类型转换 产生)double dd = 1.21;int i = dd;//int& pp = dd;const int& pp = dd;return 0;
} 

 上述引用,都是引用的临时对象,(a+b)结果保存在一个临时对象中,类型转换时也会产生临时对象来存储中间值,C++规定临时对象具有常性,所以想要引用就会触发权限放大,必须使用常引用

所谓的临时对象就是编译器需要一个空间暂存表达式的求值结果时创建的一个未命名的对象,C++中把这个未命名对象叫做临时对象。引用后临时对象生命周期就跟着引用走临时对象一般在,两数加减乘除等,函数传值返回,类型转换时产生

 6. 指针和引用的关系

在实践中,功能具有重叠性,但是各有自己的特点,互相不可代替。

在语法概念上,引用是一个变量取别名不额外开辟空间,指针是存储一个变量地址,要开空间。

引用在定义时必须初始化,指针建议初始化,但不是必须的

引用在初始化时引用一个对象后,就不能再引用其他对象;而指针可以再不断改变指向对象。

引用可以直接访问指向对象,指针需要解引用才能访问指向对象

sizeof中含义不同,引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下站4个字节,64位下占8字节)

指针经常出现空指针和野指针问题,引用很少出现,使用起来相对安全些

引用的底层实现与指针其实没有什么区别

三. 内联函数

概念

⽤inline修饰的函数叫做内联函数,编译时C++编译器会在调⽤的地⽅展开内联函数,这样调⽤内联函数就不需要建⽴栈帧了,就可以提⾼效率。用来替换C语言中的宏函数,因为宏函数也会在预处理时替换展开,但宏函数的实现很复杂很容易出错,且不方便调试2,C++设计inline的目的就是替代C的宏函数

 使用

 nline对于编译器⽽⾔只是⼀个建议,也就是说,你加了inline编译器也可以选择在调⽤的地⽅不展开,不同编译器关于inline什么情况展开各不相同,因为C++标准没有规定这个。inline适⽤于频繁调⽤的短⼩函数,对于递归函数,代码相对多⼀些的函数,加上inline也会被编译器忽略

 因为其本质上就是空间换时间,假设内联函数本身有1000条指令,有100个位置调用他,不展开的话是 1000+100条指令 ,展开的话是1000*100条指令

#include<iostream>
#include"stack.h"
using namespace std;inline int Add(int a, int b)
{int s = a + b;return s;
}int main()
{int s = Add(5, 2);cout << Add(5, 2) * 5 << endl;return 0;
}

 vs编译器debug版本下面默认是不展开inline的,这样方便调试,可以修改,步骤如下

 注意

inline不建议声明和定义分离到两个⽂件,分离会导致链接错误。因为inline被展开,就没有函数地址,链接时会出现报错。使用内联函数时声明处不能展开,所以只能找其地址(相当于内联属性没有了),但函数实现那里又认为自己是内联函数所以出现错误

 四. nullptr

NULL实际上是一个宏,在传统的C头文件(stddef.h)中,可以看到如下代码:

#ifndef NULL    #ifdef __cplusplus#define NULL    0    #else       #define NULL    ((void *)0) #endif
#endif

C++中NULL可能被定义为字⾯常量0,或者C中被定义为无类型指针(void*)的常量。不论采取何种定义,在使用空值的指针时,都不可避免的会遇到⼀些麻烦,如下代码

#include<iostream>
#include"stack.h"
using namespace std;void fff(int x)
{cout << "整数" << endl;
}void fff(int* x)
{cout << "指针" << endl;
}int main()
{fff(1);fff(NULL);fff((int*)NULL);//fff((void*)NULL);return 0;
}

上述代码结果为

本想通过fff(NULL);调⽤指针版本的fff(int* x);函数,但是由于NULL被定义成0调⽤了fff(int x);,因此与程序的初衷相悖。fff((void*)NULL);调用则会报错

C++11中引⼊nullptr来代替NULL,nullptr是⼀个特殊的关键字,nullptr是⼀种特殊类型的字⾯量,值是空指针它的类型可以转换成任意其他类型的指针类型。使用nullptr定义空指针可以避免类型转换的问题,因为nullptr只能被隐式地转换为指针类型,不会被隐式转换为整数类型


这篇文章就到这里啦,如果帮到你可以点点赞

(๑′ᴗ‵๑)I Lᵒᵛᵉᵧₒᵤ❤

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

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

相关文章

LDR6020双盲插便携显示器应用

随着USB Type-C接口的普及&#xff0c;越来越多的手机和笔记本电脑都支持通过C接口输出视频。这个小巧而精密的接口&#xff0c;大有把传统的HDMI和DisplayPort接口取而代之的架势。特别是usb4的推出&#xff0c;更是为USB TYPE-C接口一统有线接口形态奠定了基础。 单USB-C接口…

python入门课程Pro(1)--数据结构及判断

数据结构及判断 第1课 复杂的多向选择1.if-elif-else2.if嵌套3.练习题&#xff08;1&#xff09;大招来了&#xff08;2&#xff09;奇数还是偶数&#xff08;3&#xff09;简洁代码 第2课 数据与判断小结1.变量2.格式化输出3.逻辑运算-或与非4.判断条件5.练习题&#xff08;1&…

【手撕数据结构】拿捏双向链表

目录 链表介绍初始化链表销毁链表查找节点打印链表增加节点尾插头插在指定位置之后插入节点 删除节点尾删头删删除指定位置节点 链表判空 链表介绍 前面说到&#xff0c;链表的结构一共有八种&#xff1a;带头单向循环链表、带头单向非循环链表、带头双向循环链表、带头双向非…

【初阶数据结构】5.栈和队列

文章目录 1.栈1.1 概念与结构1.2 栈的实现2.队列2.1 概念与结构2.2 队列的实现3.栈和队列算法题3.1 有效的括号3.2 用队列实现栈3.3 用栈实现队列3.4 设计循环队列 1.栈 1.1 概念与结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操…

C语言宏定义格式化控制台打印

写了个简单的控制台打印代码&#xff0c;有三种打印级别 DEBUG INFO ERROR&#xff0c;支持颜色打印&#xff0c;支持时间打印 在MSVC环境中使用 #include <time.h> #include <string.h> #include <stdio.h>/* log level */ #define LOG_LEVEL_DEBUG (1) #d…

【STM32 HAL库】全双工I2S+双缓冲DMA的使用

1、配置I2S 我们的有效数据是32位的&#xff0c;使用飞利浦格式。 2、配置DMA **这里需要注意&#xff1a;**i2s的DR寄存器是16位的&#xff0c;如果需要发送32位的数据&#xff0c;是需要写两次DR寄存器的&#xff0c;所以DMA的外设数据宽度设置16位&#xff0c;而不是32位。…

一文带你读懂MLIR论文,理解MLIR设计准则.

论文MLIR: Scaling Compiler Infrastructure for Domain Specific Computation MLIR&#xff1a;针对特定领域计算扩展编译器基础设施 文章目录 论文MLIR: Scaling Compiler Infrastructure for Domain Specific Computation1. 论文下载2. TVM关于MLIR的讨论3. 论文正文0. 摘要…

02互联网行业的产品方向(2)

数字与策略产品 大数据时代&#xff0c;数据的价值越来越重要。大多数公司开始对内外全部数据进行管理与挖掘&#xff0c;将业务数据化&#xff0c;数据资产化&#xff0c;资产业务化&#xff0c;将数据产品赋能业务&#xff0c;通过数据驱动公司业务发展&#xff0c;支撑公司战…

Unity VR开发入门:探索虚拟现实世界的无限可能

目录 引言 Unity VR开发基础 1. 安装Unity与VR SDK 2. 创建VR项目 3. 理解VR场景结构 Unity VR开发实战 1. 场景搭建 2. 交互设计 创建C#脚本 编写VRInteractor脚本 应用脚本到场景 注意 修改VRInteractor脚本 3. 用户体验优化 4. 测试与调试 引言 随着科技的飞速…

docker: No space left on device处理与迁移目录

简介&#xff1a;工作中当遇到Docker容器内部的磁盘空间已满。可能的原因包括日志文件过大、临时文件过多或者是Docker容器的存储卷已满&#xff0c;需要我们及时清理相关文件&#xff0c;并对docker的路径进行迁移。 历史攻略&#xff1a; centos&#xff1a;清理磁盘空间 …

记录些MySQL题集(17)

一、MySQL索引为何使用B树结构&#xff1f; MySQL的索引机制中&#xff0c;默认使用BTree作为底层的数据结构&#xff0c;但为什么要选择B树呢&#xff1f;有人会说树结构是以二分法查找数据&#xff0c;所以会在很大程度上提升检索性能&#xff0c;这点确实没错&#xff0c;但…

C++初学者指南-5.标准库(第一部分)--标准库查询存在算法

C初学者指南-5.标准库(第一部分)–标准库查询存在算法 文章目录 C初学者指南-5.标准库(第一部分)--标准库查询存在算法any_of / all_of / none_ofcountcount_if相关内容 不熟悉 C 的标准库算法&#xff1f; ⇒ 简介 any_of / all_of / none_of 如果在输入范围(所有元素…

解决django与sqlite3不兼容报SQLite 3.9.0 or later is required错的问题

今天在尝试用pytest进行django的单元测试&#xff0c;pytest用的数据库是sqlite3&#xff0c;在window环境下测试得好好的&#xff0c;但是放到linux环境下就报错&#xff0c;具体是报django.core.exceptions.ImproperlyConfigured: SQLite 3.9.0 or later is required (found …

GPT-LLM

本心、输入输出、结果 文章目录 GPT-LLM前言国际公司AI发展概览国内公司AI发展概览GPT-LLM 编辑 | 简简单单 Online zuozuo 地址 | https://blog.csdn.net/qq_15071263 如果觉得本文对你有帮助,欢迎点赞、收藏、评论 前言 国际公司AI发展概览 公司主要AI贡献与产品特点OpenAI…

【LeetCode】day17:654 - 最大二叉树, 617 - 合并二叉树, 700 - 二叉树搜索树中的搜索, 98 - 验证二叉搜索树

LeetCode 代码随想录跟练 Day17 654.最大二叉树617.合并二叉树700.二叉搜索树中的搜索98.验证二叉搜索树 654.最大二叉树 题目描述&#xff1a; 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点&#xff0c;其值为 nums 中的…

SpringBoot整合SSE,实现后端主动推送DEMO

前言 说起服务端主动推送&#xff0c;大家第一个想到的一定是WEBSOCKET 。 作为软件工程师&#xff0c;不能无脑使用一种技术&#xff0c;要结合实际情况&#xff0c;择优选取。 SSE&#xff08;Server-Sent Events&#xff09;相比于WEBSOCKET 1、轻量化、兼容性 基于传统…

pytorch学习(十二)c++调用minist训练的onnx模型

在实际使用过程中&#xff0c;使用python速度不够快&#xff0c;并且不太好嵌入到c程序中&#xff0c;因此可以把pytorch训练的模型转成onnx模型&#xff0c;然后使用opencv进行调用。 所需要用到的库有&#xff1a; opencv 1.完整的程序如下 import torch from torch impo…

零基础STM32单片机编程入门(十七)SPI总线详解及RC522-NFC刷卡模块实战含源码

文章目录 一.概要二.SPI总线基本概念1.SPI总线内部框图2.总体特征3.通讯时序 三.RC522介绍1.NFC基本介绍2.RC522模块基本特点3.RC522模块原理图4.RC522模块SPI通讯时序 四.RC522模块读卡实验五.CubeMX工程源代码下载六.小结 一.概要 SPI总线是由Motorola公司提出&#xff0c;是…

05_解封装和解码

1. 基本概念 容器就是一种文件格式&#xff0c;比如flv、mkv、mp4等。包含下面5种流以及文件头信息。 流是一种视频数据信息的传输方式&#xff0c;5种流&#xff1a;音频&#xff0c;视频&#xff0c;字幕&#xff0c;附件&#xff0c;数据。 包在ffmpeg中代表已经编码好的一…

FPGA实验3:D触发器设计

一、实验目的及要求 熟悉Quartus II 的 VHDL 文本设计简单时序电路的方法&#xff1b; 掌握时序电路的描述方法、波形仿真和测试&#xff0c;特别是时钟信号的特性。 二、实验原理 运用Quartus II 集成环境下的VHDL文本设计方法设计简单时序电路——D触发器&#xff0c;依据…