【C++】入门基础【下】

目录

  • 一、缺省参数
  • 二、函数重载
    • 1. 函数类型不同
    • 2. 参数个数不同
    • 3、函数类型顺序不同
  • 三、引用
    • 1、引用的概念和定义
    • 2、引用的功能
      • 2.1 功能1: 做函数形参,修改形参影响实参
      • 2.2 功能2: 做函数形参,减少拷贝,提高效率
      • 2.3 功能3:引用做返回值类型,修改返回对象
      • 2.4 功能4: 引用做返回值类型,减少拷贝,提高效率
    • 3、引用的特性
    • 4、`const`引用
    • 5、指针和引用的关系
    • 6、`inline`
    • 7、`nullptr`

【C++】入门基础【上】<–请点击
在这里插入图片描述
个人主页<—请点击
C++专栏<—请点击

一、缺省参数

  • 缺省参数是声明或定义函数时为函数的参数指定⼀个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参,缺省参数分为全缺省半缺省参数。(有些地方把缺省参数也叫默认参数)。

下面我们通过代码来认识一下缺省参数。

void func(int a = 10)
{cout << a << endl;
}

我们知道,在C语言中函数定义中变量是不能给值的,但在C++中却可以,这时候,给定的这个值就是缺省值,那这个参数就是缺省参数

那了解了缺省参数是什么,那么它在使用的时候又该怎么用能?请看代码:

func();
func(5);

此时我们要调用我们定义的函数,并且一个不给实参,一个给实参,让我们一起看一下代码的运行情况。
在这里插入图片描述
可以看到,没给实参的,函数默认使用了缺省值,而给定实参的,函数使用的是给定的实参而在C语言中,如果我们不给实参但函数又需要实参时,此时程序就会报错,所以C++的缺省参数优化了这一问题。

  • 全缺省就是全部形参给缺省值,半缺省就是部分形参给缺省值C++规定半缺省参数必须从右往左依次连续缺省不能间隔跳跃给缺省值

如果你这样定义代码:

void func1(int a = 100, int b)
{cout << a<< " "<< b << endl;
}

会报错:
在这里插入图片描述
因为半缺省参数必须从右往左依次连续缺省

  • 带缺省参数的函数调用,C++规定必须从左到右依次给实参,不能跳跃给实参
    在这里插入图片描述
    像上图打算只给第二个传实参,跳过第一个时就会报错。

  • 函数声明和定义分离时缺省参数不能在函数声明定义同时出现规定必须函数声明缺省值

void func2(int a = 100);
void func2(int a = 100)
{cout << a << endl;
}

在这里插入图片描述
应改为:

void func2(int a = 100);
void func2(int a)
{cout << a << endl;
}

二、函数重载

我们知道C语言中不支持在同一作用域中出现同名函数的,它会报错,但在C++中是被允许的。C++支持在同⼀作用域中出现同名函数但是要求这些同名函数的形参不同,可以是参数个数不同或者类型不同如果全部相同那在C++中也是不被允许的

1. 函数类型不同

void func(int x, int y)
{cout << x + y << endl;
}void func(double x, double y)
{cout << x + y << endl;
}

测试结果:
在这里插入图片描述
从上面的测试结果,可以看出函数的调用成功而且正确

2. 参数个数不同

void f()
{cout << "hello world!" << endl;
}void f(int a)
{cout << a << endl;
}

测试结果:
在这里插入图片描述
注意:这里的第二个函数的参数不能带缺省值,如果带缺省值的话,当我们调用f()时,第一个函数和第二个函数都满足,此时程序会报出下面的错误:
在这里插入图片描述
这样编译器就会不知道调用谁,所以我们写重载函数的时候一定要注意区分它们,不能让它们存在歧义。

3、函数类型顺序不同

void fd(int a, double b)
{cout << "fd(int a, double b)" << endl;
}void fd(double b, int a)
{cout << "fd(double b, int a)" << endl;
}

测试结果
在这里插入图片描述

三、引用

1、引用的概念和定义

引用不是新定义⼀个变量,而是给已存在变量取了⼀个别名编译器不会为引用变量开辟内存空间,它和它引用的变量共用同⼀块内存空间。

引用的用法类型& 引⽤别名 = 引⽤对象;

C++中为了避免引入太多的运算符,会复用C语言的⼀些符号,引用取地址使用了同⼀个符号&,大家注意使用方法角度区分就可以。

#include<iostream>
using namespace std;int main()
{int a = 10;//b和c是a的别名int& b = a;int& c = a;cout << a << endl;b++;cout << a << endl;c++;cout << a << endl;return 0;
}

结果:
在这里插入图片描述

#include<iostream>
using namespace std;int main()
{int a = 10;//b和c是a的别名int& b = a;int& c = a;//d也是a的别名int& d = b;cout << &a << endl;cout << &b << endl;cout << &c << endl;cout << &d << endl;return 0;
}

在这里插入图片描述
它们的地址都是一模一样的。

2、引用的功能

2.1 功能1: 做函数形参,修改形参影响实参

我们在C语言阶段实现交换函数的时候是用指针来实现的,现在引用也可以起到这样的作用。

#include<iostream>
using namespace std;void Swap(int& a, int& b)
{int tmp = a;a = b;b = tmp;
}int main()
{int x = 10;int y = 20;Swap(x, y);cout << "x=" << x << endl;cout << "y=" << y << endl;return 0;
}

交换结果:
在这里插入图片描述

2.2 功能2: 做函数形参,减少拷贝,提高效率

不使用引用时,值传递会触发原对象的拷贝,如果对象比较大(如包含复杂成员,动态内存或嵌套结构),拷贝操作的时间和空间开销显著。

而当使用引用时,引用就是原对象的别名,传递时不会产生拷贝,直接操作原始对象,省去了拷贝构造的开销,尤其对大型对象效果显著。

2.3 功能3:引用做返回值类型,修改返回对象

#include <iostream>
using namespace std;int& func(int* a, int n)
{return a[n];
}void print(int* a,int n)
{for (int i = 0;i < n;i++){cout << a[i]<<" ";}cout << endl;
}int main()
{int a[11];for (int i = 0;i < 11;i++){a[i] = i + 1;}print(a, 11);func(a, 2)+=10;print(a, 11);
}

从上面代码中可以看出,func函数的返回值类型是引用充当的,这样的好处是可以更改返回对象。

运行结果:
在这里插入图片描述
从运行结果可以看出数组中下标为2的位置被更改了。

2.4 功能4: 引用做返回值类型,减少拷贝,提高效率

我们知道当函数调用结束时函数栈帧会被销毁,那么其中定义的局部变量的生命周期也就结束了自然也会被销毁,当函数的返回值是函数中定义的局部变量时,编译器会将返回值拷贝下来,然后储存在临时变量中作为返回值

假设有如下函数:

int func()
{int set = 10;return set;
}

在这里插入图片描述
那既然传引用返回可以更改返回对象,那传值返回可以吗?下面我们来试一下。我们还是使用传引用返回的代码,但是将传引用返回改为传值返回。

发现程序会报出以下错误:
在这里插入图片描述
这是因为函数返回的是值,而在函数返回值之前同样会将返回的值拷贝下来,储存在临时变量中进行返回,而临时变量它具有常性,是不可修改的,所以才会报出以上错误。

下图是它们三者之间的区别
在这里插入图片描述
所以说引用做返回值类型,减少了拷贝,提高了效率。

产生临时变量的情况:

出现类型转换的时候也会产生临时变量,像double类型d=1.5,转化为int类型的x这种情况,会产生一个临时变量存储转换结果,然后再将临时变量赋值给x

产生临时变量的情况有以下几种:类型转换、值传递、表达式求值等等。
其中值传递就是我们上图展示的情况,表达式求值例如:

int a = 1;
int b = 9;
int c = a + b * 10;

计算b*10时生成临时int,再与a相加生成临时int,最后赋值给c

不安全的引用写法:

int& func()
{int set = 10;return set;
}

原因:set是局部变量,func结束后,set就销毁了,返回它的别名本质也是一种类似野指针的行为。

3、引用的特性

  • 引用在定义时必须初始化;
  • ⼀个变量可以有多个引用;
  • 引用⼀旦引用⼀个实体,再不能引用其他实体。

引用无法改变指向所以在链式结构中无法替代指针,这样的场景下必须使用指针。

4、const引用

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

权限放大的错误样例

const int x = 10;
int& y = x;

在这里插入图片描述
最初定义的x的本意就是x不可更改,但却使用int&引用x这就导致了冲突,它的权限被放大了。

正确的使用:

const int x = 10;
const int& y = x;

注意以下这种情况中没有权限的放大

const int x = 10;
const int& y = x;
int z = y;

有人在学习完成引用后他们会认为上面这段代码涉及到了权限的放大,但上面代码的意图是定义一个变量z,并将y值赋值给z,只是一个简单的赋值操作,注意不要混淆了。

权限的缩小是被允许的

就像老师对你说,下课不准出校门,但你连教室门都不出这种情况一样。

int x = 10;
const int& y = x;

5、指针和引用的关系

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

指令汇编角度引用是使用指针实现的。

int x = 10;
int& y = x;
int* py = &x;

转到指令汇编
在这里插入图片描述
引用的下面两行是引用语句的汇编代码,而指针下面两行是指针语句的汇编代码。我们从上图可以看出两者一模一样,所以进一步印证了引用是使用指针实现的。

6、inline

inline修饰的函数叫做内联函数,编译时C++编译器会在调用的地方展开内联函数,这样调用内联函数就不需要建立函数栈帧了,就可以提高效率

C语言实现的宏函数也会在预处理时替换展开**,但是宏函数实现很复杂且很容易出错,还不能调试,C++设计inline目的就是替代C语言的宏函数。**

正常的函数:

int add(int x, int y)
{int sum=x + y;return sum;
}

执行语句:int ret = add(2, 3);时,它的反汇编代码是这样的:
在这里插入图片描述
图片中有一个call指令,这个指令是调用add函数,说明函数没有在预处理时展开

inline修饰的函数:

inline int add(int x, int y)
{int sum=x + y;return sum;
}

执行语句:int ret = add(2, 3);时,它的反汇编代码是这样的:
在这里插入图片描述
从上图可以看出它没有调用函数,也就是没有创建函数栈帧,而是在预处理阶段就展开了,像C语言的宏函数一样。这样就可以提高效率。

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

inline不建议声明定义分离到两个文件,分离会导致链接错误。因为inline被展开,就没有函数地址,链接时会出现报错。我们知道编译器生成可执行程序会经过预处理、编译、汇编、链接等过程,而在链接过程代码中的普通函数的地址需要到XXX.o符号表中去寻找,因为他们不会展开所以它们的函数地址会进入符号表中,当你正确使用inline修饰函数,即声明和定义不分离时,inline修饰的函数调用的地方已经正常展开了,而当你声明和定义分离时,由于inline修饰的函数的地址它本身不会进入XXX.o符号表,又因为你没有inline修饰的函数定义,此时函数调用的地方就没有正常展开编译器寻找地址的时候又找不到,此时就会报链接错误

声明和定义分离的错误情况:
F.h

#include <iostream>
using namespace std;inline int add(int x, int y);

F.cpp

#include "F.h"inline int add(int x, int y)
{int sum = x + y;return sum;
}

test.cpp

#include"F.h"int main()
{int ret = add(2, 3);return 0;
}

链接错误
在这里插入图片描述
当你声明和定义不分离,再将F.cpp中的定义删去,(因为此时F.h中已经有了,如果你这里不删除的话它依旧会报错,因为出现了两个一摸一样的函数主体),即:

F.h

#include <iostream>
using namespace std;inline int add(int x, int y)
{int sum = x + y;return sum;
}

这样就可以正常展开了。

拓展inlinestatic所修饰的函数都具有内部链接属性,不会进入XXX.o符号表中,所以不会造成C2084类型错误:
在这里插入图片描述

7、nullptr

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

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

上面这段代码是条件编译指令,感兴趣的小伙伴点击–>【C语言】编译和链接、预处理详解

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

#include <iostream>
using namespace std;void f(int n)
{cout << "f(int n)" << endl;
}
void f(int* ptr)
{cout << "f(int* ptr)" << endl;
}int main()
{f(0);f(NULL);return 0;
}

这段代码的执行结果是:
在这里插入图片描述本想通过f(NULL)调⽤指针版本的f(int*)函数,但是由于NULL定义成0,调用了f(int x),因此与程序的初衷相悖f((void*)NULL);调用会报错
在这里插入图片描述
为了解决这个问题C++11中引入了nullptrnullptr是⼀个特殊的关键字nullptr是⼀种特殊类型的字面量,它可以转换成任意其他类型的指针类型。使用nullptr定义空指针可以避免类型转换的问题,因为nullptr只能被隐式地转换为指针类型,而不能被转换为整数类型。

#include <iostream>
using namespace std;void f(int n)
{cout << "f(int n)" << endl;
}
void f(int* ptr)
{cout << "f(int* ptr)" << endl;
}int main()
{f(0);f(NULL);f(nullptr);return 0;
}

在这里插入图片描述
这样就解决了这个问题。所以在C++初始化指针为空,会用nullptr这个关键字初始化。

总结:
以上就是本期博客分享的全部内容啦!如果觉得文章还不错的话可以三连支持一下,你的支持就是我前进最大的动力!
技术的探索永无止境! 道阻且长,行则将至!后续我会给大家带来更多优质博客内容,欢迎关注我的CSDN账号,我们一同成长!
(~ ̄▽ ̄)~

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

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

相关文章

git比较不同分支的不同提交文件差异

背景&#xff1a;只想比较某2个分支的某2次提交的差异&#xff0c;不需要带上父提交。 以commitA为基准&#xff0c;用commitB去比较差异 直接上代码&#xff1a; commitAxxxx1 commitBxxxx2 outputFile"output.txt"# 获取与第一个父提交的文件列表 filesA$(git di…

Linux内核之struct pt_regs结构

前沿 项目开发最近进行系统hook功能实现相关业务&#xff0c;主要在centos7和8系列环境开发下关功能。调研了相关知识点&#xff0c;发现在系统7和8上内核版本差别比较大&#xff0c;7-3.10.x系列版本&#xff0c;8-4.18.x系列版本。依据两个系统的内核情况根对应的内核符号表进…

《从混乱到有序:ArkUI项目文件结构改造指南》

在ArkUI开发的广袤天地里&#xff0c;构建一个清晰、有序的文件结构&#xff0c;是打造优质应用的关键。一个合理的文件结构&#xff0c;就像为开发者精心绘制的地图&#xff0c;在项目的各个阶段&#xff0c;都能提供明确的指引&#xff0c;让开发过程顺畅无阻。今天&#xff…

C#基于Sunnyui框架和MVC模式实现用户登录管理

C#基于Sunnyui框架和MVC模式实现用户登录管理 1 Controller1.1 UserManagementController.cs&#xff08;控制器入口&#xff09; 2 Model2.1 UserRepository.cs&#xff08;用户管理模型&#xff09;2.2 User.cs&#xff08;用户结构体&#xff09;2.3 SQLiteHelper.cs&#x…

自然语言处理(NLP)技术的实例

自然语言处理&#xff08;NLP&#xff09;技术在各个领域都有广泛的应用&#xff0c;以下是几个例子&#xff1a; 语音识别&#xff1a;通过NLP技术&#xff0c;计算机可以识别和理解语音指令&#xff0c;例如智能助手如Siri和Alexa就是通过语音识别技术实现与用户的交互。 机…

Spring Boot实战(三十六)编写单元测试

目录 一、什么是单元测试&#xff1f;二、Spring Boot 中的单元测试依赖三、举例 Spring Boot 中不同层次的单元测试3.1 Service层3.2 Controller 层3.3 Repository层 四、Spring Boot 中 Mock、Spy 对象的使用4.1 使用Mock对象的背景4.2 什么是Mock对象&#xff0c;有哪些好处…

aws服务(四)文件存储服务S3 介绍使用代码集成

一、介绍 1、简介 Amazon S3 是 Amazon Web Services 提供的一种对象存储服务(Object Storage),用于在云中存储和检索任意数量的数据。它以高可用性、高扩展性和高持久性著称,非常适合用来存储网站资源、数据备份、日志文件、大数据、机器学习输入输出等。 2、主要特性 …

应用信息1.13.0发布

增加工具箱 增加启动器功能 增加布局查看器 增加手动安装和卸载应用 增加APK文件解析 增加应用多选功能 增加查看应用预装版本 增加应用信息和ADB命令导出 修复其它问题... 百度下载&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;1234

【Vue3 实战】插槽封装与懒加载

一、为什么需要插槽&#xff1f;从一个面板组件说起 在电商首页开发中&#xff0c;经常遇到这样的场景&#xff1a; 「新鲜好物」「人气推荐」同样类型模块都需要相同的标题栏&#xff0c;但内容区布局不同 这时候&#xff0c;插槽&#xff08;Slot&#xff09;就像一个「内容…

虚无隧穿产生宇宙(true nothing tunneling) 是谁提出的

是 亚历克斯.维连金 英文名&#xff08;alex vilenkin 或者 Alexander Vilenkin)提出来的。 “虚无隧穿产生宇宙”&#xff08;true nothing tunneling&#xff09;这一概念并非一个标准的物理学术语&#xff0c;它更像是对某些现代宇宙学理论的描述&#xff0c;尤其是涉及宇宙…

postgis:添加索引时提示“对访问方法 gist 数据类型 geometry 没有默认的操作符表“

问题 在对gis表的geom字段创建空间索引时&#xff0c;出现“对访问方法 "gist" 数据类型 geometry 没有默认的操作符表”的提示报错。 解决方案 按系列步骤进行排查并解决。 1.先确认已安装postgis -- 查看postgis版本 SELECT postgis_full_version() 若安装了则…

图论---Prim堆优化(稀疏图)

题目通常会提示数据范围&#xff1a; 若 V ≤ 500&#xff0c;两种方法均可&#xff08;朴素Prim更稳&#xff09;。 若 V ≤ 1e5&#xff0c;必须用优先队列Prim vector 存图。 #include <iostream> #include <vector> #include <queue> #include <…

代码随想录算法训练营第一天:数组part1

今日学习的文章链接和视频链接 ● 自己看到题目的第一想法 ● 看完代码随想录之后的想法 ● 自己实现过程中遇到哪些困难 ● 今日收获&#xff0c;记录一下自己的学习时长 状态 思路理解完成 30% 代码debug完成 60% 代码模板总结并抽象出来 100% 题目 704 二分查找 题目链接…

企业为何要求禁用缺省口令?安全风险及应对措施分析

在当今数字化时代&#xff0c;企业网络安全面临着前所未有的挑战。缺省口令的使用是网络安全中的一个重要隐患&#xff0c;许多企业在制定网络安全红线时&#xff0c;明确要求禁用缺省口令。本文将探讨这一要求的原因及其对企业安全的重要性。 引言&#xff1a;一个真实的入侵场…

PostgreSQL 中的权限视图

PostgreSQL 中的权限视图 PostgreSQL 提供了多个系统视图来查询权限信息&#xff0c;虽然不像 Oracle 的 DBA_SYS_PRIVS 那样集中在一个视图中&#xff0c;但可以通过组合以下视图获取完整的系统权限信息。 一 主要权限相关视图 Oracle 视图PostgreSQL 对应视图描述DBA_SYS_…

【防火墙 pfsense】1简介

&#xff08;1&#xff09; pfSense 有以下可能的用途&#xff1a; 边界防火墙 路由器 交换机 无线路由器 / 无线接入点 &#xff08;2&#xff09;边界防火墙 ->要充当边界防火墙&#xff0c;pfSense 系统至少需要两个接口&#xff1a;一个广域网&#xff08;WAN&#xff0…

数据库+Docker+SSH三合一!深度评测HexHub的全栈开发体验

作为一名技术博主&#xff0c;我最近一直被各种开发工具切换搞得焦头烂额。数据库要用Navicat&#xff0c;服务器管理得开Termius&#xff0c;Docker操作还得切到命令行&#xff0c;每天光在不同工具间切换就浪费了大量时间。直到团队里的一位架构师向我推荐了HexHub这个一体化…

第十天 Shader编程:编写简单表面着色器 Addressable资源管理系统 DOTS(面向数据技术栈)入门

前言 作为Unity初学者&#xff0c;在实现复杂场景时经常会遇到性能瓶颈。本文将带你通过四个关键技术的实战学习&#xff0c;掌握现代Unity开发的核心优化方案&#xff1a; Shader编程 - 编写表面着色器控制物体渲染Addressable系统 - 实现高效资源管理DOTS技术栈 - 解锁百万…

项目自动化测试

一.设计测试用例(细致全面) 二.先引入所需要的pom.xml依赖 1.selenium依赖 2.webdrivermanager依赖 3.commons-io依赖 编写测试用例–按照页面对用例进行划分,每个页面是Java文件,页面下的所有用例统一管理 三.common包(放入公用包) 类1utils 可以调用driver对象,访问url …

ap无法上线问题定位(交换机发包没有剥掉pvid tag)

一中学&#xff0c;新开的40台appoe交换机核心交换机旁挂ac出口路由的组网&#xff0c;反馈ap无法上线&#xff0c;让协助解决。 组网如下&#xff1a; 排查过程&#xff1a; 检查ac的配置&#xff0c;没有发现问题 发现配置没有问题&#xff0c;vlan1000配置子接口&#xff…