当前位置: 首页 > news >正文

震惊!一男子深夜燥热难耐,竟然偷偷起身打开电脑并开始 学习c++入门基础(下)

1. 引用

1.1基本概念

引用,就是给已经存在的变量取别名

编译器不会为引用变量开辟新的空间,而是和被引用的变量公用一块内存空间。

使用方式: 类型& 引用别名= 被引用对象;

C++为了避免产生过多的的运算符,于是用的是和取地址相同的符号&,使用时需要注意区分。

#include<iostream>
using namespace std;int main()
{int a = 0;int& b = a;int& c = b;c++;cout << &a << endl;cout << &b << endl;cout << &c<< endl;cout << a << endl;cout << c << endl;
}

 cb45cc5b8941447aadf09cbf6eead2eb.png

可以看到引用变量的地址都是一样的,并且改变了引用对象的值就改变了被引用对象的值。

1.2 注意

我们要区分开三个概念:

        1.引用是给变量取别名
        2.typedef是给类型取别名
        3.define是常量替换

1.3 特性

• 引⽤在定义时必须初始化

• ⼀个变量可以有多个引⽤

• 引⽤⼀旦引⽤⼀个实体,再不能引⽤其他实体

#include<iostream>
using namespace std;
int main()
{int a = 10;// 编译报错:“ra”: 必须初始化引⽤//int& ra;int& b = a;int c = 20;// 这⾥并⾮让b引⽤c,因为C++引⽤不能改变指向,// 这⾥是⼀个赋值b = c;cout << &a << endl;cout << &b << endl;cout << &c << endl;cout << a << endl;cout << b << endl;cout << c << endl;return 0;
}

ed08365620e049b199e75b7df5d39d6b.png

 1.4 引用的使用

• 引用主要用于引用传参和引用做返回值中减少拷贝提高效率,在改变引用对象的同时改变被引用对象。

• 引⽤传参跟指针传参功能是类似的,引⽤传参相对更⽅便⼀些。

引⽤和指针在实践中相辅相成,功能有重叠性,但是各有特点,互相不可替代。

以下代码段是一个引用替代指针传参的例子

rx,ry形参是实参x,y的别名,所以改变rx,ry就是改变了x,y。

void swap(int& rx, int& ry)
{int temp = rx;rx = ry;ry = temp;
}
int main()
{int x = 0, y = 1;cout << x << " " << y << endl;swap(x, y);cout << x << " " << y << endl;return 0;
}

1.5 const引用

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

int main()
{const int a = 10;// 编译报错:error C2440: “初始化”: ⽆法从“const int”转换为“int &”// 这⾥的引⽤是对a访问权限的放⼤//int& ra = a;// 这样才可以const int& ra = a;// 编译报错:error C3892: “ra”: 不能给常量赋值//ra++;// 这⾥的引⽤是对b访问权限的缩⼩int b = 20;const int& rb = b;// 编译报错:error C3892: “rb”: 不能给常量赋值//rb++;cout << b<< " " << rb << endl;b++;cout << b << " " << rb << endl;return 0;
}

我们发现rb++会产生报错,因为rb这个引用变量是被const修饰的,因此他的权限小不能改变自生的值,然而我们b变量并没有被const修饰,因此我们可以进行b++的操作,又因为rb为b的引用变量,因此rb的值也因此改变。

 编译器需要⼀个空间暂存表达式的求值结果时临时创建的⼀个未命名的对象,C++中把这个未命名对象叫做临时对象。

需要注意的是类似 int& rb = a*3这样⼀些场景下a*3的和结果保存在⼀个临时对象中

double d = 12.34; int& rd = d也是类似,在类型转换中会产⽣临时对象存储中间值,也就是说,rb和rd引⽤的都是临时对象,在类型转换的传参时,是先将d的整数部分传给临时变量,再将临时变量的值传给我们要赋值的对象的。

⽽C++规定临时对象具有常性,所以这⾥就触发了权限放⼤,必须要⽤常引⽤才可以

int main()
{int a = 10;const int& ra = 30;// 编译报错: “初始化”: ⽆法从“int”转换为“int &”// int& rb = a * 3;const int& rb = a * 3;double d = 12.34;// 编译报错:“初始化”: ⽆法从“double”转换为“int &”// int& rd = d;const int& rd = d;cout << d << " " << rd << endl;return 0;
}

1.6 引用和指针的关系

C++中指针和引⽤在实践中他们相辅相成,功能有重叠性,但是各有⾃⼰的特点,互相不可替代。

• 语法概念上引⽤是⼀个变量的取别名不开空间,指针是存储⼀个变量地址,要开空间

• 引⽤在定义时必须初始化,指针建议初始化,但是语法上不是必须的

• 引⽤在初始化时引⽤⼀个对象后,就不能再引⽤其他对象;⽽指针可以在不断地改变指向对象。

• 引⽤可以直接访问指向对象,指针需要解引⽤才是访问指向对象。

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

• 指针很容易出现空指针和野指针的问题,引⽤很少出现,引⽤使⽤起来相对更安全⼀些

2. inline

⽤inline修饰的函数叫做内联函数,编译时C++编译器会在调⽤的地⽅展开内联函数,这样调⽤内联函数就需要建⽴栈帧了,就可以提⾼效率。

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

• C语⾔实现宏函数也会在预处理时替换展开,但是宏函数实现很复杂很容易出错的,且不⽅便调试,C++设计了inline⽬的就是替代C的宏函数

• vs编译器 debug版本下⾯默认是不展开inline的,这样⽅便调试,debug版本想展开需要设置⼀下以下两个地⽅。

• inline不建议声明和定义分离到两个⽂件,分离会导致链接错误。因为inline被展开,就没有函数地址,链接时会出现报错,所以建议直接就放在头文件里面定义声明。

 这里我们可以通过汇编观察程序是否展开,有call Add语句就是没有展开,没有就是展开了

inline int Add(int x, int y)
{int ret = x + y;ret += 1;ret += 1;ret += 1;return ret;
} 
int main()
{	int ret = Add(1, 2);cout << Add(1, 2) * 5 << endl;return 0;
}

 实现⼀个ADD宏函数的常⻅问题,以下三种方式都是错误的
#define ADD(int a, int b) return a + b;
#define ADD(a, b) a + b;
#define ADD(a, b) (a + b)

为什么不能加分号?
        define宏的本质是原模原样的替换,编译器在替换完cout << ADD(1, 2) << endl;会
        分号也替换过去,则会出现报错
为什么要加外⾯的括号?
       避免替换 ADD(1, 2) * 5 的语句时变成 1+ 2 * 5,这样顺序就错乱了
 为什么要加⾥⾯的括号?
        避免ADD(x & y, x | y)的语句变成了 x & y + x | y,因为加号的优先级是更高的,所以会导致运算顺序错乱。

下面是正确的宏定义 

#include<iostream>
using namespace std;// 正确的宏实现
#define ADD(a, b) ((a) + (b))int main()
{int ret = ADD(1, 2);cout << ADD(1, 2) << endl;cout << ADD(1, 2) * 5 << endl;int x = 1, y = 2;ADD(x & y, x | y); return 0;
}

3. nullptr

 NULL的本质是⼀个宏,在传统的C头⽂件(stddef.h)中,可以看到如下代码

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

由此我们可以发现

• C++中NULL可能被定义为字⾯常量0,或者C中被定义为⽆类型指针(void*)的常量。不论采取何种定义,在使⽤空值的指针时,都不可避免的会遇到⼀些⿇烦,本想通过f(NULL)调⽤指针版本的f(int*)函数,但是由于NULL被定义成0,调⽤了f(int x),因此与程序的初衷相悖。f((void*)NULL);调⽤会报错。

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

简单的来说我们可以将nullptr当作C++中的空指针使用,这样可以避免出现以下的带啊吗问题

957dbcbaf2b647aba194adf5557d2f35.png

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 一个很变态却非常实用的发论文的新方向,【Transformer+目标检测】
  • 为什么有的地方笔记本经常连不上wifi,而手机可以?
  • Linux学习第56天:RGB转HDMI
  • Radiant Photo 1.4.1 AI智能完美照片修图插件支持PS ai beta
  • 珠海市举办“数智赋能产业转型与创新培训专场”活动
  • 【天机学堂】面试总结
  • 繁简之争:为什么手机芯片都是 ARM
  • 【Material-UI】Autocomplete 组件的局限性(Limitations)详解
  • [最短路SPFA]--启动!!!!!
  • 微信小程序开发优惠券制作源码
  • 分享一个基于人脸识别的小区物业管理系统Spring Boot(源码、调试、LW、开题、PPT)
  • Cocos Creator2D游戏开发(10)-飞机大战(8)-计分和结束
  • Oracle的RAC集群安装和配置 NFS 共享存储(NAS共享存储)
  • pytest之fixture
  • 过期知识:thinkphp5 使用migrate给现有的数据表新增表字段
  • Angular js 常用指令ng-if、ng-class、ng-option、ng-value、ng-click是如何使用的?
  • extjs4学习之配置
  • Git 使用集
  • JAVA多线程机制解析-volatilesynchronized
  • js 实现textarea输入字数提示
  • leetcode98. Validate Binary Search Tree
  • Mac转Windows的拯救指南
  • October CMS - 快速入门 9 Images And Galleries
  • SpringCloud集成分布式事务LCN (一)
  • unity如何实现一个固定宽度的orthagraphic相机
  • 案例分享〡三拾众筹持续交付开发流程支撑创新业务
  • - 概述 - 《设计模式(极简c++版)》
  • 计算机在识别图像时“看到”了什么?
  • 猫头鹰的深夜翻译:Java 2D Graphics, 简单的仿射变换
  • 七牛云假注销小指南
  • 浅析微信支付:申请退款、退款回调接口、查询退款
  • 区块链分支循环
  • 如何设计一个微型分布式架构?
  • 新版博客前端前瞻
  • ​DB-Engines 12月数据库排名: PostgreSQL有望获得「2020年度数据库」荣誉?
  • ​必胜客礼品卡回收多少钱,回收平台哪家好
  • ​草莓熊python turtle绘图代码(玫瑰花版)附源代码
  • ​如何使用QGIS制作三维建筑
  • #Js篇:单线程模式同步任务异步任务任务队列事件循环setTimeout() setInterval()
  • #调用传感器数据_Flink使用函数之监控传感器温度上升提醒
  • #免费 苹果M系芯片Macbook电脑MacOS使用Bash脚本写入(读写)NTFS硬盘教程
  • (2024)docker-compose实战 (9)部署多项目环境(LAMP+react+vue+redis+mysql+nginx)
  • (C语言)strcpy与strcpy详解,与模拟实现
  • (delphi11最新学习资料) Object Pascal 学习笔记---第13章第6节 (嵌套的Finally代码块)
  • (ZT)北大教授朱青生给学生的一封信:大学,更是一个科学的保证
  • (附源码)计算机毕业设计SSM疫情居家隔离服务系统
  • (回溯) LeetCode 78. 子集
  • (原創) 如何動態建立二維陣列(多維陣列)? (.NET) (C#)
  • (转)http-server应用
  • (转)真正的中国天气api接口xml,json(求加精) ...
  • (转载)虚幻引擎3--【UnrealScript教程】章节一:20.location和rotation
  • .NET 8.0 发布到 IIS
  • .net core使用RPC方式进行高效的HTTP服务访问
  • .NET 使用 JustAssembly 比较两个不同版本程序集的 API 变化
  • .NET 同步与异步 之 原子操作和自旋锁(Interlocked、SpinLock)(九)