C++入门(三)引用,内联,auto+nullptr

C++入门(三)引用,内联,auto+nullptr,第1张

目录

一、引用

定义

举例

特性

常引用

取别名原则

权限放大

权限相等

权限缩小

注意

 使用场景

做参数

做返回值

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

引用作为返回值

总结

和指针的比较

区别

底层相同

二、内联函数

概念

为什么要用inline?

例如:ADD函数

使用示例

inline可调试

 更改如下

 特性

三、auto关键字

定义

用法

一般例子

注意

输出变量类型

 指定auto的类型

同一行定义多个变量

auto正确用法

范围for循环

c语言的for更改数组的值

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

解决方法:引用

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

auto不能推导的场景

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


一、引用 定义

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


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

在类型前加&

举例

特性

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


 

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

        如举例所示


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

 

常引用

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

取别名原则

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

权限放大

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

权限相等

权限缩小

注意

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

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

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

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

而临时变量具有常性,上述例子中,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;
}

可以减少拷贝,提高效率

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

先来看一个例子:

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

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

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

如下:

引用作为返回值

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

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

证明:

但是上述代码是不正确的

存在非法访问!!!!

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

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

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

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

总结

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

和指针的比较
区别

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


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


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


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


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


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


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


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

底层相同

二、内联函数 概念

以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

 更改如下

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

对宏,没有什么改变

 对inline,可支持调试

 特性

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

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

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

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

 或者

再调用

三、auto关键字 定义

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

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

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

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

用法 一般例子
#include 
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

例如:

即使直接使用a也不行

输出变量类型

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

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

typeid(变量名).name()

 指定auto的类型

指定auto为指针和引用

同一行定义多个变量

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

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

定义时变量须是相同类型 

auto正确用法

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

2.范围for循环

范围for循环

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

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

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

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

 原因:

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

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

解决方法:引用

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

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

方法如下:

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

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

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

auto不能推导的场景

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

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

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

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

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

 四、指针空值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;
}

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

NULL 是空指针,应输出6

结果并非预期值

用nullptr可以解决这个问题

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

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/langs/914562.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-16
下一篇2022-05-16

发表评论

登录后才能评论

评论列表(0条)

    保存