IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++入门(三)引用,内联,auto+nullptr -> 正文阅读

[C++知识库]C++入门(三)引用,内联,auto+nullptr

目录

一、引用

定义

举例

特性

常引用

取别名原则

权限放大

权限相等

权限缩小

注意

?使用场景

做参数

做返回值

函数的返回值是一个临时变量

引用作为返回值

总结

和指针的比较

区别

底层相同

二、内联函数

概念

为什么要用inline?

例如:ADD函数

使用示例

inline可调试

?更改如下

?特性

三、auto关键字

定义

用法

一般例子

注意

输出变量类型

?指定auto的类型

同一行定义多个变量

auto正确用法

范围for循环

用c语言的for更改数组的值

那么c++中局部for循环可以直接做到吗?

解决方法:引用

?范围for循环必须使用auto吗?

auto不能推导的场景

?四、指针空值nullptr(C++11)


一、引用

定义

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


与之前学过的取地址共用符号:&

在类型前加&

举例

5739314a6adf4944be4d06389fbf5aa5.png

特性

1. 引用在定义时必须初始化

0c6d8a0e1b614b79bba6a70423835b0b.png
?

2. 一个变量可以有多个引用

? ? ? ? 如举例所示


3. 引用一旦引用一个实体,再不能引用其他实体

?384696d6659f445592943f4e05672cbc.png

常引用

在变量前 或 引用前加 const ,使其变成常量后加引用或变成常引用

取别名原则

对原引用的变量,权限只能缩小或相等,不能放大。

权限放大

0746b8fdb3f6411a9e3b7b28a9879dc9.png

取别名相当于改,是增大权限的操作,常量是不支持的,因此会报错。

权限相等

f07b839ce4bc4b5ca5acc27b782248ae.png

权限缩小

9e222228763b4c52aa74d238192b855b.png

注意

1.常量直接加别名需要加const

24622f2582564b299037eb93d6f34c75.png

常量是不可改的,不加const相当于权限的放大,加const是权限相等的操作

b65fb22ed52f4812ac540bcde060db87.png

?2.给变量赋予不同类型的别名要加const

b1b818f6c0ba42b386166cd4fdd1df48.png

50aaf97b1f314f38ae0a9fa9ff90b989.png

?原因:不同类型间的相互转换,会发生截断或者提升,截断或提升之后的值是存放在临时变量中的。

而临时变量具有常性,上述例子中,c其实是临时变量的别名,具有常性,不加const属于权限的放大,加上const则是权限相等的操作。

?使用场景

做参数

如下:可配合函数重载使用

void Swap(int& left, int& right)
{
	int temp = left;
	left = right;
	right = temp;
}

void Swap(double& left, double& right)
{
	double temp = left;
	left = right;
	right = temp;
}

int main()
{
	int a = 5, b = 3;
	Swap(a,b);
	double c = 2.3, d = 5.5;
	Swap(c,d);
	return 0;
}

可以减少拷贝,提高效率

做返回值

函数的返回值是一个临时变量

先来看一个例子:

e9693cceaa6d42c7937d3f47b04db083.png

Count函数的返回值,直接引用是不通过的

因为函数是先调用,完成后n会销毁,因此传给ret的是一个临时变量

而临时变量具有常性,要引用前面必须加const

如下:

52833f86bbf1489183703c1bbd4a8c0a.png

引用作为返回值

859a3f589b044566bbc6ffd4bd078d4e.png

函数返回类型是一个引用的话,那么返回的就相当于是 n 的别名,别名是不用开辟临时变量的。

用引用 ret 接收函数的返回值,此时的 ret 相当于是返回值? n? 别名的别名。ret 和 n 是同一个地址。

证明:

c08572a761e44426a3ccb60a8a695048.png

但是上述代码是不正确的

存在非法访问!!!!

函数先调用,调用完后赋上新的别名,但是注意,函数调用完后,变量n就已经销毁了,空间已经还给系统了,但是我们通过别名访问到了已经还给系统的空间,属于非法访问

再次调用,就会发现同样的地址,其中的内容已被系统更新

4174e59ff2224adf910222be28762602.png

通过上述内容,我们知道,引用返回可以减少拷贝,提高效率,但是我们如何防止这种 “野引用”问题的出现

保证返回变量不销毁,内存空间不还给系统即可,可以用到static函数

5e03a4c64fb94ceca069ecb509887a08.png

总结

如果函数返回时,出了函数作用域,如果返回对象还未还给系统,则可以使用引用返回,如果已经还给系统了,则必须使用传值返回,防止出现越界问题。
?

和指针的比较

区别

1.引用在定义时必须初始化,指针无要求


2. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体


3. 没有NULL引用,但有NULL指针


4. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数


5. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小


6. 有多级指针,但是没有多级引用


7. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理


8. 引用比指针使用起来相对更安全

底层相同

92aa50c51f2c4180b4fcf34fa2c9fee9.png

二、内联函数

概念

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

在c语言中,如果一个函数多次调用,那么每次调用都要开辟栈帧,效率会大幅降低。c语言的解决方案是用宏来实现函数的功能,在调用时直接替换展开,就没有了压栈开销,提高效率。

为什么要用inline?

在c中,函数是容易写的,但是用宏来实现函数,往往会出现问题,最常见的就是替换后的优先级问题。

例如:ADD函数

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

用宏来实现:

#define ADD(x,y) ((x)+(y))

错误示范:

//和函数搞混 ,分号滥用
//#define ADD(int x,int y) return x+y;

//#define ADD(x,y) return x+y;

//#define ADD(x,y) (x+y);

//不带括号,会出现优先级问题
//#define ADD(x,y) x+y

而c++中 inline 的出现就是为了解决宏难理解,且容易写错的问题。

使用示例

直接在函数前加 inline 即可,不容易写错。

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

inline可调试

c语言中宏是不支持调试的

但c++中的inline是可以调试的,需要改一些属性。

在Debug版本下,inline 和 宏 在编译中都不会展开,直接是 call ADD

c5e2c38ef9824e60959414b671963072.png

?更改如下

3c192178e5b5432985819bad6e69f3f9.png

ce6f46f93989419990336a318e3794a4.png

197a9792bf8548fb9e3902923d9b253c.png

完成上述操作后,再次进行调试,转到反汇编:

对宏,没有什么改变

4b935f3406b44e4ca7f1f770e4ffa8ed.png

?对inline,可支持调试

35d04e76310843d59dc1f71ed7be5e92.png

?特性

1.inline是一种以空间换时间的做法,省去调用函数开辟栈帧。代码很长或者有循环/递归的函数不适宜使用作为内联函数。

2.编译器会自动优化,如果定义为inline的函数体内有循环/递归等等,编译器优化时会忽略掉内联。

dafc72049e6f40d68063b67de4954ba5.png

454b79e1df3d479cb78a02ddb523bd15.png

?
3.inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。

a478f28b026c47b3a69e6ea166be967a.png

解决方案,将声明和定义写在一起

1a695ba84d30454cb0aba9845cda0949.png

?或者

34e8a794207b4489b5342f723c26f36d.png

再调用

1bd8c441ccdf438b8086be2d3bfd9bf0.png

三、auto关键字

定义

在c和早期c++中auto关键字几乎不怎么用,只是用来修饰局部变量,并且常被忽略,如下:

21624e36ebbe424cb7e4129d7d5444d5.png

?在c++11中,auto 有了新的功能:

作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。

意思就是,当我们不知道一个变量的类型时,将他前面的类型替换为auto,编译器会根据自动推导出此数据的类型。

用法

一般例子

#include <iostream>
using namespace std;

int Test()
{
	return 6;
}


int main()
{
    //这里编译器会自动识别a是int类型
	auto a = 2;

	double b = 3.8;
    
    //编译器会自动识别出c是double类型
	auto c = b;

    //自动推出ret是int类型
    auto ret = Test();  
   
	return 0;
}

注意

不能直接auto+变量,不允许没有初始化的变量使用auto

例如:

1c4c1eb935be48158ad6fed3042fd9e3.png

即使直接使用a也不行

2a7bbd55a24e4d4abf6f368e8e2381eb.png

输出变量类型

?如何判定编译器已经识别出来变量的类型呢?

记住输出变量类型的固定格式:

typeid(变量名).name()

769fc5ed7cfc42e29993142e70b59f67.png

?指定auto的类型

指定auto为指针和引用

035a76ccdd1e4ae4bd353c7965d3bc0a.png

同一行定义多个变量

变量必须是相同的类型
否则会报错

2ac39f9b4f634937934e24673461719a.png

编译器会推导出第一个的变量的类型并使用,会产生冲突

定义时变量须是相同类型?

auto正确用法

1.当类型特别长时,可以使用auto让编译器直接进行推导,这里在之后的笔记中会记录下来。

2.范围for循环

范围for循环

c916fadd897446c89883168487879016.png

依次自动取数组中的数据,赋值给变量e,自动结束,更加方便

在这里可以对比一下更改数组内容时的操作

用c语言的for更改数组的值

ea7df03ff1ed442daf91c5dac97f9070.png

那么c++中局部for循环可以直接做到吗?

7f1c269b56fd4a489bea7091adfb2e30.png

?原因:

e是一个变量,对变量进行操作,是不影响原数组中的数据的,因此这样写,数组中的数据不会被影响。

变量可以随便取,不用非得是e

解决方法:引用

既然e是变量,对原内容无影响,那么可以将变量变为数组元素的别名

对e操作即是对数组内容的操作

方法如下:

bf80900b898946c89133d298d7da06a3.png

?范围for循环必须使用auto吗?

不是的,只要是数组元素类型即可,整型数组--int ,字符类型--char ……

什么数组类型都可以用auto,更加统一方便

auto不能推导的场景

1.不能做函数参数和函数返回值

// 编译失败
void Test(auto a)
{}

//auto不能作为形参类型,因为编译器无法对a的实际类型进行推导

e83551d124d1468f9255b3a2714b0134.png

2. auto不能直接用来声明数组

void Test()
{
int a[] = {1,2,3};
auto b[] = {4,5,6};
}

50d7a5e46f8a48dcb83391fd0d19cd7b.png

?四、指针空值nullptr(C++11)

?c++中空指针推荐使用 nullptr

因为在c++中,NULL和 0是不规范的

NULL实际是一个宏,在传统的C头文件(stddef.h)中会有:

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

因此c++中NULL和0是一个东西,会有缺陷,比如:

void f(int x)
{
	cout << 5 << endl;
}

void f(int* x)
{
	cout << 6 << endl;
}


int main()
{
	f(0);
	f(NULL);
	return 0;
}

4556895dc7d6450e87798fff500da69c.png

在我们的认识中,0是整型 ,输出5

NULL 是空指针,应输出6

结果并非预期值

用nullptr可以解决这个问题

cd2fb21020b5488b9b0be8801ffceb1b.png

?因此在c++中写空指针的话,推荐使用nullptr

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-05-14 09:47:23  更:2022-05-14 09:47:27 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 19:13:51-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码