初识C++(二)

六、引用

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

通俗地讲,可以理解为一个人能够拥有多个称呼,这些所有的称呼都是表示这一个人的。

用法:类型& 引用变量名(对象名) = 引用实体;

注意:引用类型与引用实体的类型必须一致!

引用使用的注意事项

①引用在定义时必须初始化

②引用初始化后,无法再改变指向

③一个引用实体可以拥有多个引用/别名

下面是关于注意事项的图例:

引用使用举例

交换函数Swap:

那么问题来了:既然能够使用引用来替换指针的一些用途,引用能否完全替换掉指针呢?

不能的!C++设计出引用,是对指针使用复杂的场景进行一些替换,让代码简单易懂,并不能完全替换指针,引用不能够完全替换指针的根本原因在于引用不能够改变其指向。

对于Java/Python而言,是不存在指针的,这两门语言的引用能够改变指向,因此替换掉了指针。

也正是因为C++的引用不能够改变指向,对于链表、二叉树等基于指针链接前后物理不连续的空间的数据结构,也是不能使用引用替换掉指针的!

引用的使用场景

1.做参数

①输出型参数;②对象比较大时,使用引用能够减少拷贝、提高效率。

以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低。 

2.做返回值

①修改返回对象;②减少拷贝,提高效率。

错误示例

首先,C语言中学到过,当我们在函数中返回临时变量/局部变量的值时,由于函数栈帧的回收,会导致返回的该值是一个随机值,指针中讲到的野指针也是这样类似的情况。

以下是错误的示例

上图就返回了函数中创建的一个临时变量的值,这样做是错误的。

那么同样的,我们也不能够在上图的返回类型使用引用类型,同样为错误示例,如下所示:

上图函数Func返回类型int&,引用类型的返回,同样不能够作用在临时变量/局部变量上!否则相当于对一块未经允许访问的空间进行了访问,本质是野引用!将这个未经访问的空间a中的值赋给ret,那么ret就是随机值。

同样,ret是别名的错误示例

返回变量出了函数作用域,生命周期到头,需要销毁(栈帧回收) ,因此不能够使用引用返回!

如局部变量与临时变量。

正确示例

引用不能作用于临时变量、局部变量,那么引用一般作用于什么呢?

引用能够作用于静态变量(如static修饰)、全局变量、堆区开辟空间的变量。

首先在C++中,变量一般被称之为对象,同时C++将结构体升级成了类:结构体中不再只能够定义变量/对象,还能够定义函数。这意味着对于一些数据结构的实现,不再需要采取结构定义与实现函数分离的形式,而是可以直接在类中定义结构和函数进行操作

类中定义的函数名也较C语言简洁,因为该函数存在于哪个类,就属于哪个类的函数,不需要用复杂的名称来与其他的结构中的函数进行区分!

拿顺序表简单地举个例子: 

顺序表的类:

//正确示范
//C++中的结构体中能够定义对象和函数,结构体一般称之为类
//同时C++不需要使用typedef去除struct,允许直接使用struct后名称
struct SeqList
{int* a;int size;int capacity;//相较于C语言的函数操作更为简便void Init()//初始化{int* tmp = (int*)malloc(sizeof(int) * 4);if (tmp == NULL){perror("malloc fail");return;}a = tmp;size = 0;capacity = 4;}void PushBack(int x)//尾插{//CheckMemorySize(省略)a[size++] = x;}int& Get(int pos)//获取pos下标的数据、使用int&返回别名,也能够修改pos处的数据{assert(pos >= 0);assert(pos < size);return a[pos];}void Destroy()//销毁{free(a);a = NULL;}
};

如上我们可以看见,在SeqList类中直接定义初始化、尾插、获取任意位置数据并修改等函数,在形参部分和整体代码量相较于C语言简洁便利许多。同时C++不需要使用typedef去除struct,直接使用类型名即可。

同时,对于获取pos下标处数据与修改,在C语言中本是两个函数分别完成,但是现在可以使用int&的引用类型作为返回值,一个函数就能够搞定,因为它返回的是该下标处数据的别名,对其的修改能够直接体现在数据的修改上。

传值返回,返回的是变量的临时拷贝;传引用返回,返回的是变量的别名。

正因如此,传值无法做到直接改变目标变量,但是传引用却能够轻松完成。

简单插入4个数据对其进行一些数据操作如下图:

如果我们将Get函数的返回类型改为int,那么就会返回目标下标数据的拷贝,即一份临时变量。

临时变量具有常性,即常量性,无法被修改!

如上图,使用引用作为返回值是能够起到很重要的作用的。

引用与指针的区别

引用在底层转换为指针的解释如下图: 

我们对比引用与指针在汇编层面上的指令,发现引用经过编译过程后也被转换为了指针,那么就说明,在底层上,引用是会开空间的,语法层面上引用是别名,不开空间。

引用底层使用指针实现的,引用的语法含义与底层实现是背离的!

通俗点,就像鱼香肉丝中没有鱼,老婆饼也不是老婆做的一样。

七、内联函数

如果调用函数次数过多、需要创建的函数栈帧过多,那么为了提高效率,C语言中通过宏函数的方式来替换函数;而C++通过内联函数的方式使函数在外部直接展开而不用创建栈帧。

对于宏而言,需要注意:1.宏并不是函数;2.宏属于预处理指令,末尾不需要分号,本质是一种替换;3.额外重视括号!括号控制优先级。

宏在预处理阶段就会被替换!

对于一个加法的Add函数,写成宏函数如下:

为什么x、y要单独括号括起来?

因为x、y不一定代表一个值,可能代表一个表达式!如果x、y是表达式,且表达式中的运算符优先级低于+号,那么替换进去就会出现问题!举例如下:

因此我们需要加括号确保绝对的正确顺序。

宏的优缺点?

优点:1.增强代码的复用性;2、提高效率/性能

缺点:1、宏在预处理阶段就被替换,不方便调试;2、可读性差,可维护性差,容易误用;3、没有类型安全的检查。

C++替代宏的技术?

1、常量定义---换用const enum;2、短小函数定义,换用内联函数。

内联函数的概念

inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开没有函数调
用建立栈帧的开销,内联函数提升程序运行的效率。

未使用inline修饰时,调用Add函数的反汇编代码如下图:

在汇编层面上,我们看到编译器会去找函数Add的指令首地址,从而调用函数。

如果在上述函数前增加inline关键字将其改成内联函数,在编译期间编译器会用函数体替换函数的调用,不调用函数,也就不用创建函数栈帧,消除了函数栈帧创建的开销:

release下:直接查看反汇编下汇编代码是否存在call Add

debug下:我们需要将属性-常规-调试信息格式改为程序数据库(/Zi),属性-优化-内联函数扩展改为只适用于_inline(/Ob1),这样就能够调试-反汇编查看了

那么使用inline修饰Add函数,使其成为内联函数后,内联函数展开的反汇编如下:

通俗来讲,内联函数就是将函数里面的运算逻辑灵活地在外面实现,而不去创建调用函数所需要的栈帧空间。

内联特点

内联本质上是向编译器发出请求,当函数较大,编译器就会忽略内联的请求,较小函数能够正常内联。

因此较小函数的多次调用,我们可以使用内联inline,较大函数则使用静态static。

1. inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用。缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运行效率。

2. inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。

3. inline不建议声明与定义分离,分离的情况会导致链接错误,inline展开函数,就不会在符号表中生成该函数的地址,链接时就无法找到对应函数。

#pragma once解决头文件重复引入的问题,但是并不能解决两个.cpp文件中都包含相同头文件,该头文件又包含某个函数的定义,如下图所示,那么就会存在函数的重定义问题。

有三种方式能够解决这一问题: 

①声明与定义分离

如果不采用声明与定义分离,只通过定义,那么可以使用static或者inline进行操作。 

②采用static静态链接,只在当前文件可见 

③采用inline内联,同理只在当前文件展开

八、auto关键字---C++11

auto:自动推导类型。

对于auto而言,必须要初始化,不然怎么自动推导呢?

实际意义:对于较长类型,可以使用auto简便替代:

如下图的函数指针类型:

再如其他较长的类型:

auto的利弊

优点:简化较长的类型。

缺点:对不熟悉当前代码的人而言,一定程度上影响代码可读性。

注:①C++规定auto不能定义数组。②慎用auto,C++目前允许auto作返回值,但是不建议。

③同一行使用auto声明多个变量,这些变量必须同类型,否则编译器不会通过,编译器只对第一个类型进行推导,然后用该类型定义后面的变量。

上图中auto a = 9 , k = 'c' ;就无法编译通过,因为a与k类型不同但是他俩想用一个auto。

auto不能推导的场景

①auto不能作为函数的参数

②auto不能直接用来声明数组

九、范围for循环---C++11

范围for循环的使用

for(auto e : array)
{cout << e ;
}
cout << end;

auto自动推导数组中数据类型,也可以自己填入数组数据类型;e是数组数据的依次的临时拷贝。

范围for循环会自动迭代、自动判断结束。

范围for循环可以使我们的数组遍历过程变得非常简便:

范围for循环的使用条件

①for循环迭代的范围必须确定。

数组即第一个元素到最后一个元素的范围;对于类而言,应该提供begin和end的方法,begin和end就是for循环迭代的范围。

②迭代的对象要实现++和==的操作

十、指针空值nullptr---C++11

在C++中,NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量。

因此C++11中创建了nullptr关键字,用于表示指针空值,就不用NULL来表示指针空值了,以避免可能出现的差错。

注:

①在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的    ②在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。                                        ③为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。

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

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

相关文章

【RedisStack】Linux安装指南

【RedisStack】Linux安装指南.md 前言下载解压创建启动文件设置密码把密码设置到环境变量启动/停止相关命令测试&验证官网资料参考资料 前言 Redis Stack是使用Redis的最佳起点。我们将我们必须提供的最好的技术捆绑在一起&#xff0c;形成一个易于使用的软件包。Redis St…

达梦8-DMSQL程序设计学习笔记1-DMSQL程序简介

1、DMSQL程序简介 DMSQL程序是达梦数据库对标准SQL语言的扩展&#xff0c;是一种过程化SQL语言。在DMSQL程序中&#xff0c;包括一整套数据类型、条件结构、循环结构和异常处理结构等&#xff0c;DMSQL程序中可以执行SQL语句&#xff0c;SQL语句中也可以使用DMSQL函数。 DMSQ…

STM32 FreeRTOS 基础知识

多任务处理 内核是操作系统的核心组件。诸如 Linux 这样的操作系统采用的内核&#xff0c; 看似允许用户同时访问计算机。很明显&#xff0c;多个用户可以同时执行多个程序。 每个执行程序都是受操作系统控制的任务&#xff08;或线程&#xff09;。如果一个操作系统能够以这…

T-SQL编程

目录 1、T-SQL的元素 1.1 标识符 1. 常规标识符 2. 分隔标识符 1.2 变量 1. 全局变量 2. 局部变量 1.3 运算符 1. 算数运算符 2. 赋值运算符 3. 位运算符 4. 比较运算符 5. 逻辑运算符 6. 字符串连接运算符 7. 一元运算符 8. 运算符的优先级和结合性 1.4 批处…

js中的Object.defineProperty()详解

文章目录 一、Object.defineProperty()二、descriptor属性描述符2.1、数据描述符2.2、访问器描述符2.3、descriptor属性2.3.1、value2.3.2、writable2.3.3、enumerable &#xff08;可遍历性&#xff09;2.3.4、configurable &#xff08;可配置性&#xff09; 三、注意事项 一…

【搭建JavaEE】(2)Tomcat安装配置和第一个JavaEE程序

Tomcat–容器(Container) 下载 Apache Tomcat - Welcome! 下载完成 请求/响应 结构 测试 查看Jdk版本 改端口号localhost8080–>8099 学学人家以后牛逼了可以用自己名字当文件夹名 配置端口8099 找到server文件 用记事本打开 再打开另一个logging文件 ”乱码解决“步骤&…

centos7.6 安装nginx 1.21.3与配置ssl

1 安装依赖 yum -y install gcc zlib zlib-devel pcre-devel openssl openssl-devel2 下载Nginx wget http://nginx.org/download/nginx-1.21.3.tar.gz3 安装目录 mkdir -p /data/apps/nginx4 安装 4.1 创建用户 创建用户nginx使用的nginx用户。 #添加www组 # groupa…

高级软件工程-复习

高级软件工程复习 坐标国科大&#xff0c;下面是老师说的考试重点。 Ruby编程语言的一些特征需要了解要能读得懂Ruby程序Git的基本命令操作知道Rails的MVC工作机理需要清楚&#xff0c;Model, Controller, View各司什么职责明白BDD的User Story需要会写&#xff0c;SMART要求能…

TrollFools 2.10-22 插件注入工具 官方版

《TrollFools巨魔设备专用插件注入工具》这是一款专为巨魔设备打造的插件注入神器&#xff0c;功能强大且操作便捷。它能够轻松地将插件注入通过AppStore商店下载的任意APP中&#xff0c;同时也能随时卸载&#xff0c;丝毫不影响APP的正常使用。注入后的APP仍可正常更新&#x…

30分钟内搭建一个全能轻量级springboot 3.4 + 脚手架 <1> 5分钟快速创建一个springboot web项目

快速导航 <1> 5分钟快速创建一个springboot web项目 <2> 5分钟集成好最新版本的开源swagger ui&#xff0c;并使用ui操作调用接口 <3> 5分钟集成好druid并使用druid自带监控工具监控sql请求 <4> 5分钟集成好mybatisplus并使用mybatisplus generator自…

arcgis中生成格网矢量带高度

效果 1、数据准备 (1)矢量边界(miain.shp) (2)DEM(用于提取格网标高) (3)DSM(用于提取格网最高点) 2、根据矢量范围生成格网 模板范围选择矢量边界,像元宽度和高度根据坐标系来输入,我这边是4326的,所以输入的是弧度,输出格网矢量gewang.shp 3、分区统计 …

海豚调度DolphinScheduler-3.1.9配置windows本地开发环境

源代码下载地址https://dolphinscheduler.apache.org/zh-cn/docs/3.1.9 1.Zookeeper安装与使用 如图下载解压zookeeper安装包&#xff0c;并创建data和log目录 下载地址 https://archive.apache.org/dist/zookeeper/zookeeper-3.6.4/apache-zookeeper-3.6.4-bin.tar.gz 进入…

P1图文解析:初识算法和数据结构

文章目录 前言1、算法例子1.1、查字典&#xff08;二分查找算法&#xff09;1.2、整理扑克&#xff08;插入排序算法&#xff09;1.3、货币找零&#xff08;贪心算法&#xff09; 2、算法与数据结构2.1、算法定义2.2、数据结构定义2.3、数据结构与算法的关系2.4、独立于编程语言…

校园跑腿小程序---轮播图,导航栏开发

hello hello~ &#xff0c;这里是 code袁~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f981;作者简介&#xff1a;一名喜欢分享和记录学习的在校大学生…

UE材质节点Fresnel

Fresnel节点 ExponentIn 控制边缘透明度 BaseReflectFractionIn 控制中心透明度

浅谈云计算07 | 云安全机制

浅谈云计算安全机制&#xff1a;全方位守护云端世界 一、引言二、加密技术&#xff1a;数据的隐形护盾三、散列机制&#xff1a;数据完整性的忠诚卫士四、数字签名&#xff1a;数据来源与真伪的鉴定专家五、公钥基础设施&#xff08;PKI&#xff09;&#xff1a;信任的基石六、…

Notepad++上NppFTP插件的安装和使用教程

一、NppFTP插件下载 图示是已经安装好了插件。 在搜索框里面搜NppFTP&#xff0c;一般情况下&#xff0c;自带的下载地址容易下载失败。这里准备了一个下载连接&#xff1a;Release v0.29.10 ashkulz/NppFTP GitHub 这里我下载的是x86版本 下载好后在nodepad的插件里面选择打…

高级运维:源码编译安装httpd 2.4,提供系统服务管理脚本并测试

1.下载httpd 2.4 源码 wget https://archive.apache.org/dist/httpd/httpd-2.4.54.tar.gz 2.解压下载压缩包 tar -zxvf httpd-2.4.54.tar.gz cd httpd-2.4.54 3.安装httpd需要的依赖包 sudo yum groupinstall "Development Tools" -y sudo yum install gcc glibc ap…

8.Bridge 桥接模式(结构型模式)

【1】抽象A>实现细节b 【2】抽象A>抽象B<实现细节b 【3】【抽象B】相对稳定&#xff0c;也可能变化 【实现细节b】频繁变化 【4】抽象B 不稳定&#xff1f; 思考问题&#xff1a;一个变化是平台&#xff08;抽象B&#xff09;的变化&#xff0c;另一个变化是型号…

【PyQt】如何在mainwindow中添加菜单栏

[toc]如何在mainwindow中添加菜单栏 如何在mainwindow中添加菜单栏 主要有两种方法&#xff1a; 1.直接创建mainwindow进行添加 2.使用ui文件加载添加 第二种方法更为常见&#xff0c;可以应用到实际 1.直接创建mainwindow进行添加 import sysfrom PyQt5.QtWidgets import …