【C++】引用、内联函数、auto关键字、范围for、nullptr

news2024/9/22 19:33:13

  • 引用
    • 什么叫引用
    • 引用的特性
    • 常引用
    • 使用场景
    • 传值、传引用效率比较
    • 引用和指针的区别
  • 内联函数
  • auto关键字(C++11)
  • 基于范围的for循环(C++11)
  • 指针空值nullptr(C++11)

引用

什么叫引用

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。就像我们除了自己的大名往往还会有外号,虽然叫法不同,但是指同一个人。

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

注意:引用类型必须和引用实体是同种类型的

#include<iostream>
using namespace std;

void Test1()
{
	int a = 10;
	int& ra = a;  //定义引用类型
	cout << "变量a的地址: " << &a << endl;
	cout << "变量ra的地址: " << &ra << endl;
}

int main()
{
	Test1();
	return 0;
}

在这里插入图片描述
从上面这段代码可以看出,变量ra是变量a的引用,指向的是同一块空间。

引用的特性

1. 引用在定义时必须初始化
2. 一个变量可以有多个引用
3. 引用一旦引用一个实体,再不能引用其他实体

void Test2()
{
	int a = 10;
	// int& ra;   // 编译时会出错,因为没有初始化
	int& ra = a;
	int& rra = a;
	int b = 20;
	int& ra = b;  //编译时会出错,因为多次引用实体
	

常引用

就是引用时在前面加上const修饰,使得所指空间具有常性(常性是指被初始化一次后不能再被修改了)。要注意,引用和指针一样在使用时都有权限的问题,被引用的空间权限可以保持不变,可以缩小,但就是不能被放大。

void Test3()
{
	const int a = 10; //变量a为const修饰的常变量,具有常性
	int& ra = a; //编译时会出错,因为此处引用会使得变量a所指的空间失去常性,权限被放大了
	const int& ra = a; //编译时不会出错,因为加上了const,权限没变

	int b = 10;
	const int& rb = b; //编译时不会出错,因为没有扩大权限,只是缩小了权限,不影响
	int& rb = b; //编译时不会出错,因为权限没有被放大,和以前一样
	 
	 int& c = 10; //编译时会出错,因为10是一个被存在常量区的常量,此处引用放大了权限
	const int& c = 10;//编译时不会出错,因为引用时加了const,权限没变
}

使用场景

  1. 做函数的参数:
//函数被调用时,函数的形参名就是实参存储空间的别名
void Swap(int& left, int& right)
 {
   int temp = left;
   left = right;
   right = temp;
 }
  1. 做返回值
    要注意函数返回值用引用的前提是该函数调用执行结束,被返回的变量空间并不会被销毁归还给系统,不然会出现各种各样的问题。
//下面的变量n是静态变量,该函数执行结束时n并没有被销毁
//所以该函数的返回值可以用引用
int& Count()
{
   static int n = 0;
   n++;
   // ...
   return n; 
 }

传值、传引用效率比较

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

函数实参到形参使用值传递和引用:

#include<iostream>
using namespace std;
#include<time.h>

struct A
{
 int a[10000]; 
};

void TestFunc1(A a){}
void TestFunc2(A& a){}

void TestRefAndValue()
{
//定义一个结构体变量a,a里的数组空间很大
 A a;
 // 以值作为函数参数
 size_t begin1 = clock();
 for (size_t i = 0; i < 10000; ++i)
 TestFunc1(a);
 size_t end1 = clock();
 
 // 以引用作为函数参数
 size_t begin2 = clock();
 for (size_t i = 0; i < 10000; ++i)
 TestFunc2(a);
 size_t end2 = clock();
 
// 分别计算两个函数运行结束后的时间
 cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
 cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}

int main()
{
	TestRefAndValue();
	return 0;
}

在这里插入图片描述
值和引用的作为返回值类型的性能比较:

#include<iostream>
using namespace std;
#include <time.h>

struct A{ int a[10000]; };
A a;

// 值返回
A TestFunc1() { return a;}
// 引用返回
A& TestFunc2(){ return a;}

void TestReturnByRefOrValue()
{
 // 以值作为函数的返回值类型
 size_t begin1 = clock();
 for (size_t i = 0; i < 100000; ++i)
 TestFunc1();
 size_t end1 = clock();
 
 // 以引用作为函数的返回值类型
 size_t begin2 = clock();
 for (size_t i = 0; i < 100000; ++i)
 TestFunc2();
 size_t end2 = clock();
 
 // 计算两个函数运算完成之后的时间
 cout << "TestFunc1 time:" << end1 - begin1 << endl;
 cout << "TestFunc2 time:" << end2 - begin2 << endl;
 }

int main()
{
	TestReturnByRefOrValue();
	return 0;
}

在这里插入图片描述
通过上面的比较,我们可以发现传值和指针在作为传参以及返回值类型上效率相差很大。

引用和指针的区别

以前C语言中学习了指针,现在学习了引用,那它们两者都有哪些区别呢?
在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。

#include<iostream>
using namespace std;

int main()
{
int a = 10;
int& ra = a;
cout<<"&a = "<<&a<<endl;
cout<<"&ra = "<<&ra<<endl;
return 0; }

在这里插入图片描述
在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。
但我们可以不用去管,知道是这么个情况就行。

引用和指针的不同点:

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

内联函数

以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数提升程序运行的效率。
在这里插入图片描述如果在上述函数前增加inline关键字将其改成内联函数,在编译期间编译器会用函数体替换函数的调用。

特性:

  1. inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运行效率。
  2. inline对于编译器而言只是一个建议,不同编译器关于inline实现机可能不同,一般建议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。
  3. inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。

auto关键字(C++11)

随着程序越来越复杂,程序中用到的类型也越来越复杂,虽然我们可以使用typedef给类型取别名确实可以简化代码,但是typedef有会遇到新的难题:

typedef char* pstring;
int main()
{
 const pstring p1;    // 编译失败
 const pstring* p2;   // 编译成功
 return 0; }

在这里插入图片描述

auto简介:
在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,但是一直没有人去使用,后来C++11中,标准委员会赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。

注意:
使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto
的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编
译期会将auto替换为变量实际的类型。

使用auto要注意的地方:

  1. auto与指针和引用结合起来使用用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
int main()
{
    int x = 10;
    auto a = &x;
    auto* b = &x;
    auto& c = x;
    
    return 0;
}
  1. 在同一行定义多个变量当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
void TestAuto()
{
		//该行编译成功
    auto a = 1, b = 2; 
     // 该行代码会编译失败,因为c和d的初始化表达式类型不同
    auto c = 3, d = 4.0; 
}

auto不能推导的场景:

  1. auto不能作为函数的参数
  2. auto不能直接用来声明数组
  3. 为了避免与C++98中的auto发生混淆,C++11只保留了auto作为类型指示符的用法
  4. auto在实际中最常见的优势用法就是跟以后会讲到的C++11提供的新式for循环,还有lambda表达式等进行配合使用。

基于范围的for循环(C++11)

范围for的语法
在C++98中如果要遍历一个数组,可以按照以下方式进行:

void TestFor()
{
int array[] = { 1, 2, 3, 4, 5 };
//利用数组下标遍历数组
for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
     array[i] *= 2;
 //利用指针遍历舒数组
for (int* p = array; p < array + sizeof(array)/ sizeof(array[0]); ++p)
     cout << *p << endl; 
}

对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。

void Test()
{
int array[] = { 1, 2, 3, 4, 5 };

for(auto& e : array)
     e *= 2;
     
for(auto e : array)
     cout << e << " ";
     
return 0; 
}

注意:与普通循环类似,可以用continue来结束本次循环,也可以用break来跳出整个循环。

范围for的使用条件:
for循环迭代的范围必须是确定的对于数组而言,就是数组中第一个元素和最后一个元素的范围;还有一些其它的,等后面需要时会讲。

指针空值nullptr(C++11)

C++98中的指针空值:
在良好的C/C++编程习惯中,声明一个变量时最好给该变量一个合适的初始值,否则可能会出现不可预料的错误,比如未初始化的指针。如果一个指针没有合法的指向,我们基本都是按照如下方式对其进行初始化:

void TestPtr()
{
int* p1 = NULL;
int* p2 = 0;
// ……
}

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

#ifndef NULL
#ifdef __cplusplus  //如果是C++,那NULL就等于0
#define NULL   0
#else
#define NULL   ((void *)0)
#endif
#endif

可以看到,NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量。不论采取何种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦。
在C++98中,字面常量0既可以是一个整形数字,也可以是无类型的指针(void*)常量,但是编译器默认情况下将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转(void *)0。但C++11中却没这么麻烦,因为它有一个专门的关键字来解决这个问题。

注意:

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

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

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

相关文章

《python3网络爬虫开发实战 第二版》之基本库的使用-requests的使用 详解

文章目录1 requests库的使用1.1 准备工作1.2 实例引入1. 3 GET请求1.3.1 基本实例1.3.2 抓取网页1.3.3 抓取二进制数据1.3.4 添加请求头1.4 POST请求1.5 响应1.6 高级用法1.6.1 文件上传1.6.2 Cookie设置1.6.3 Session维持1.6.4 SSL证书验证1.6.5 超时设置1.6.6 身份认证1.6.7 …

旅游地如何搭好影视剧“顺风车”

新春伊始&#xff0c;《满江红》《三体》《狂飙》等影视剧给影片取景地带来的关注和旅游热潮引人瞩目——从太原古县城到襄阳古城墙上的“岳”字砖、从宁波博物馆到江门的历史文化街区乃至地方特产新会陈皮……都被影视剧带上了热搜。影视作品与取景地的相互成全由来已久&#…

JavaWab开发的总括以及HTML知识

一、Web开发的总括在这里我来给大家介绍一下Wab开发需要配合哪些前后端的对应语言:首先是Java(Java通常的工作):Wab开发android开发大数据开发另外,Wab开发想要学好就需要配合之前博客中的内容,如:多线程/IO/网络/数据结构/数据库......这里建议学懂前面的内容再往下走.JavaWab…

2023年,都在说软件测试饱和了,大环境不好?为何每年还会增加40万测试员?

最近和一些刚进入软件测试行业的朋友交流&#xff0c;发现了一个有趣的现象&#xff0c;那就是对这个行业很多问题的认识是一致的、片面的&#xff0c;也可以理解为误解。利用你的时间列出他们对这个行业的所有误解&#xff0c;然后结合你多年的工作经验和你交流。毕竟你是从这…

栈的压入,弹出序列-剑指Offer-java

一、题目描述输入两个整数序列&#xff0c;第一个序列表示栈的压入顺序&#xff0c;请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如&#xff0c;序列 {1,2,3,4,5} 是某栈的压栈序列&#xff0c;序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列&a…

2019蓝桥杯真题修改数组 C语言/C++

题目描述 给定一个长度为 N 的数组 A [A_1,A_2, ,A_N]&#xff0c;数组中有可能有重复出现的整数。 现在小明要按以下方法将其修改为没有重复整数的数组。小明会依次修改A_2,A_3, ,A_N当修改 A_i时&#xff0c;小明会检查 A_i是否在 A_1 ∼ A_i−1中出现过。如果出现过&#…

2023年2月访问学者博士后热门国家出入境政策变化汇总

近期关于出国的咨询量日益增多&#xff0c;出入境政策也是其中之一。所以本期知识人网小编汇总了最新访问学者和博士后关注的热门国家及地区入境政策变化&#xff0c;提供给大家。目前各国入境政策大致分为三种&#xff1a;一、 无法入境的国家如&#xff1a;摩洛哥、朝鲜等。二…

iconfont 图标如何在uniapp中的tabBar使用

注意&#xff1a; 小程序并不支持tabBar中 设置 iconfont 1. 材料准备 首先进入字体图标网址&#xff1a;iconfont-阿里巴巴矢量图标库&#xff1b;&#xff08;如果你没有登入&#xff0c;记得登入一下&#xff09; 把图标添加入购物车 添加到购物车之后-&#xff08;右上角…

逻辑回归—分类问题的操作顺序

对于二元分类问题来说&#xff0c;分类的结果和数据的特征之间仍呈现相关关系&#xff0c;但是y的值不再是连续的&#xff0c;是0&#xff5e;1的跃迁。但是在这个过程中&#xff0c;什么仍然是连续的呢&#xff1f;”是概率&#xff0c;概率是逐渐升高的&#xff0c;当达到一个…

JVM12 字节码指令集

1. 概述 2. 加载与存储指令 2.1. 局部变量压栈指令 iload 从局部变量中装载int类型值 lload 从局部变量中装载long类型值 fload 从局部变量中装载float类型值 dload 从局部变量中装载double类型值 aload 从局部变量中装载引用类型值&#xff08;refernce&#xff09; iload_0 从…

从交换机安全配置看常见局域网攻击

前言 构建零信任网络&#xff0c;自然离不开网络准入(NAC)&#xff0c;这就涉及到交换机的一些安全测试&#xff0c;于是有了此文《从交换机安全配置看常见局域网攻击》。 交换机安全配置 如本文标题所说从交换机安全配置看常见的局域网攻击&#xff0c;那么下面提到的各种攻…

leaflet 绘制多个点的envelope矩形(082)

第082个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+leaflet中如何根据多边形的几个坐标点来绘制envelope矩形。 直接复制下面的 vue+openlayers源代码,操作2分钟即可运行实现效果. 文章目录 示例效果配置方式示例源代码(共78行)安装插件相关API参考:专栏目标示例…

【Java】线程的生命周期和状态

一、通用的线程的生命周期&#xff0c;简称&#xff1a;五态模型&#xff1a; 初始状态&#xff1a;指语言层面上的创建线程&#xff0c;操作系统中还没有创建。可运行状态&#xff1a;指线程可以分配CPU执行&#xff0c;这时线程已经在系统中创建成功。运行状态&#xff1a;指…

0元搭建linux服务器(windows笔记本)

0元搭建linux服务器一.windows装Centos71.1 centos7 iso镜像1.2 准备U盘1.3 UltraISO 启动盘制作工具安装1.4 准备一台windows 机器1.5 安装过程二 、连接无线wifi三、固定wifi ip3.1 查看网络状态3.2 查看DNS3.3 查看GATEWAY3.4 设置静态IP四、一键快速安装单机版k8s五、申请域…

基于CCG算法的IEEE33配电网两阶段鲁棒优化调度matlab

目录 1 前言 2基本内容 2.1 配网两阶段鲁棒模型 2.2 求解步骤 3部分程序 4程序结果 5程序链接 1 前言 鲁棒优化是电力系统研究的热点&#xff0c;而两阶段鲁棒和分布鲁棒研究就成为各类期刊&#xff08;sci/ei/核心&#xff09;的宠儿&#xff0c;最简单的思路是通过改…

CACTER云网关无缝对接O365系统,反垃圾实力强硬!

01 客户背景 某IT互联网企业是国家认定的高新技术企业、上海市重点大数据企业。自成立以来&#xff0c;坚持以自主研发为本&#xff0c;以客户为中心&#xff0c;专注汽车保险科技&#xff0c;具备强大的研发实力&#xff0c;致力为行业提供数字化智能化车商保险业务管理综合解…

python采集最新世界大学排名, 来看看你的母校上榜没~

前言 大家早好、午好、晚好吖 ❤ ~ 本次内容: Python 采集世界大学排行榜 并做数据可视化 知识点: 动态数据抓包 requests发送请求 结构化非结构化数据解析 开发环境: python 3.8 运行代码 pycharm 2021.2 辅助敲代码 requests 第三方模块 pip install 模块名 本次文…

基于 DSP+FPGA 的高清图像跟踪系统研制

目标识别与跟踪技术是目前图像处理研究的重点方向&#xff0c;在军事和民用领域中 具有广泛的应用价值&#xff0c;如精确制导武器、导弹飞机预警等军事领域&#xff0c;如交通管理、 刑事侦查等民用领域。其中&#xff0c;如何在复杂的背景中&#xff0c;提取、识别与跟踪特定…

File类的用法和InputStream,OutputStream的用法

这里写自定义目录标题一、File类1.构造方法2.普通方法二、InputStream1.方法2.FileInputStream3.Scanner类的应用三、OutputStream1.方法2.FileOutputStream3.PrintWriter类的应用一、File类 1.构造方法 签名说明File(File parent, Stringchild)根据父目录 孩子文件路径&…

车载前摄像头学习笔记 ———— 视频编码格式

文章目录简介格式H.26XH.261H.263H.264/AVCNALU HeaderNALU PayloadSODBRBSPEBSPMPEG-XMPEG-1MPEG-2MPEG-4MPEG-7MPEG-21简介 视频是可以理解为连续的图像序列。获取的一帧即为一幅图像&#xff0c;在每一帧的数据中&#xff0c;所有的内容都是静止的。为什么看起来是运用的呢&…