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

news2024/11/27 21:00:40

目录

一. 函数重载

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.coloradmin.cn/o/1938108.html

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

相关文章

【云原生】Kubernetes微服务Istio:介绍、原理、应用及实战案例

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

Spring Boot集成Spring Batch快速入门Demo

1.什么是Spring Batch&#xff1f; Spring Batch 是一个轻量级的开源框架&#xff0c;它提供了一种简单的方式来处理大量的数据。它基于Spring框架&#xff0c;提供了一套批处理框架&#xff0c;可以处理各种类型的批处理任务&#xff0c;如ETL、数据导入/导出、报表生成等。S…

Windows 、Linux、MacOS 进程管理机制

本心、输入输出、结果 文章目录 Windows 、Linux、MacOS 进程管理机制前言Windows 进程管理机制Linux 进程管理macOS 进程管理内存不够了,几个操作系统如何处理Windows 、Linux、MacOS 进程管理机制 编辑 | 简简单单 Online zuozuo 地址 | https://blog.csdn.net/qq_15071263 …

Webstorm-恢复默认UI布局

背景 在使用Webstorm的时候,有时候进行个性化设置,如字体、界面布局等. 但是设置后的效果不理想,想要重新设置回原来的模样,却找不到设置项. 这里提供一种解决方案,恢复默认设置,即恢复到最初刚下载好后的设置. 操作步骤 步骤一:打开setting 步骤二:搜索Restore Default,找到…

AI智能名片微信小程序在IP合作中的创新应用与品牌赋能策略

摘要&#xff1a;在数字化时代&#xff0c;品牌与IP&#xff08;Intellectual Property&#xff09;的合作已成为推动品牌增长、深化市场影响力的关键策略。AI智能名片微信小程序&#xff0c;作为新兴的数字营销工具&#xff0c;凭借其智能化、便捷化、社交化的特性&#xff0c…

Kafka架构详解之分区Partition

目录 一、简介二、架构三、分区Partition1.分区概念2.Offsets&#xff08;偏移量&#xff09;和消息的顺序3.分区如何为Kafka提供扩展能力4.producer写入策略5.consumer消费机制 一、简介 Apache Kafka 是分布式发布 - 订阅消息系统&#xff0c;在 kafka 官网上对 kafka 的定义…

shell详细介绍(清晰明了)

一、shell的介绍 Shell ⼀个命令解释器&#xff0c;它接收应⽤程序/⽤户命令&#xff0c;然后调⽤操作系统内核。 Shell还是⼀个功能强⼤的编程语⾔&#xff0c;易编写、易调试、灵活性强。 (1) Linux提供的shell解释器有 (2) bash 和 sh的关系 (3) Centos默认的Shell解析器…

前端学习(三)之CSS

一、什么是CSS html定义网页的结构和信息&#xff08;骨架血肉&#xff09;css定义网页的样式&#xff08;衣服&#xff09;js定义用户和网页的交互逻辑&#xff08;动作&#xff09; 通过CSS&#xff0c;可以指定页面中各个元素的颜色、字体、大小、间距、边框、背景等样式&…

YOLOv8改进 | Neck | 注意力尺度序列融合的检测框架ASF-YOLO

秋招面试专栏推荐 &#xff1a;深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 &#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 专栏目录 &#xff1a;《YOLOv8改进有效…

spring boot(学习笔记第十四课)

spring boot(学习笔记第十四课) Spring Security的密码加密&#xff0c;基于数据库认证 学习内容&#xff1a; Spring Security的密码加密基于数据库认证 1. Spring Security的密码加密 如果用户的密码保存在数据库中是以明文保存&#xff0c;对于公司的安全将是灾难性的&…

SpringBoot中fastjson扩展: 自定义序列化和反序列化方法实战

❃博主首页 &#xff1a; 「码到三十五」 &#xff0c;同名公众号 :「码到三十五」&#xff0c;wx号 : 「liwu0213」 ☠博主专栏 &#xff1a; <mysql高手> <elasticsearch高手> <源码解读> <java核心> <面试攻关> ♝博主的话 &#xff1a…

安全防御---防火墙综合实验3

安全防御—防火墙综合实验3 一、实验拓扑图 二、实验要求 12&#xff0c;对现有网络进行改造升级&#xff0c;将当个防火墙组网改成双机热备的组网形式&#xff0c;做负载分担模式&#xff0c;游客区和DMZ区走FW3&#xff0c;生产区和办公区的流量走FW1 13&#xff0c;办公区…

【软件配置】不使用ROS系统,不进行编译,纯python环境配置rosbag,从而解析.bag文件

【软件配置】不使用ROS系统&#xff0c;不进行编译&#xff0c;纯python环境配置rosbag&#xff0c;从而解析.bag文件 【anaconda】conda创建、查看、删除虚拟环境&#xff08;anaconda命令集&#xff09;_conda 创建环境-CSDN博客 【Windows配置三】Python3.6安装rosbag_ros…

4.基础知识-数据库技术基础

基础知识 一、数据库基本概念1、数据库系统基础知识2、三级模式-两级映像3、数据库设计4、数据模型&#xff1a;4.1 E-R模型★4.2 关系模型★ 5、关系代数 二、规范化和并发控制1、函数依赖2、键与约束3、范式★3.1 第一范式1NF实例3.2 第二范式2NF3.3 第三范式3NF3.4 BC范式BC…

鸿蒙智联:一统多设备,跨端融合新魔法

在当今科技飞速发展的时代&#xff0c;操作系统的优劣直接影响着用户的体验和设备的性能。而鸿蒙系统&#xff0c;宛如一颗璀璨的新星&#xff0c;凭借其卓越的跨端能力&#xff0c;为我们展现了一个全新的科技世界。 鸿蒙系统的最大魅力之一&#xff0c;就在于它能够实现一个系…

【嵌入式开发之数据结构】树的基本概念、逻辑结构和四种常用的遍历算法及实现

树&#xff08;Tree&#xff09;的定义及基本概念 树的定义 树(Tree)是个结点的有限集合T&#xff0c;它满足两个条件&#xff1a; 有且仅有一个特定的称为根&#xff08;Root&#xff09;的节点&#xff1b;其余的节点分为个互不相交的有限合集&#xff0c;其中每一个集合又…

【无重叠空间】python刷题记录

润到贪心篇。 class Solution:def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:#十行贪心大神if not intervals:return 0#按照第第二个元素进行排序&#xff0c;贪心思想&#xff0c;参考活动安排都是以结束时间进行排序的intervals.sort(keylambda x:…

分离式网络变压器的集成化设计替代传统网络变压器(网络隔离滤波器)尝试

Hqst盈盛&#xff08;华强盛&#xff09;电子导读&#xff1a;今天分享的是应用了分离式网络变压器设计的的新型网络变压器&#xff08;网络隔离变压器&#xff09; 今天我们一起来看这款新型网络变压器&#xff0c;它就是应用分离式网络变压器集成到电路板上&#xff0c;加上外…

git协同开发与冲突解决

协同开发流程 基本的使用方法 # 1 多人开发同一个项目-张三&#xff1a;本地 版本库-李四&#xff1a;本地 版本库-我&#xff1a; 本地 版本库----远程仓库&#xff1a;本地版本库内容--》推送到远程仓库-----# 2 演示&#xff08;我是项目管理员&#xff09;-1 增加几个开发…

(二刷)代码随想录第21天|236. 二叉树的最近公共祖先

236. 二叉树的最近公共祖先 递归三部曲&#xff1a; 1、确定参数和返回值&#xff1a; class Solution {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {} 2、确定中止条件&#xff1a; if (root null || root p || root q) { // 递归结…