【我的C++入门之旅】(下)

文章介绍了C++中的引用,强调其作为变量别名的特性,以及与指针的区别。讨论了auto关键字的使用场景,特别是自动类型推导的功能。然后讲解了范围for循环简化遍历数组和容器的语法。接着提到了inline函数的概念,用于提高代码效率,但需注意可能导致代码膨胀的问题。最后,文章讨论了指针空值nullptr相对于NULL的优势。
摘要由CSDN通过智能技术生成

前言

1.引用

取别名,一块空间有多个名字或者说是一个变量有多个名字
比如:李逵,在家称为"铁牛",江湖上人称"黑旋风"。

我们可以看到下面a的地址和b的地址是一样的
也就是说a就是b,b就是a

#include <iostream>
using namespace std;
int main()
{
	int a = 1;
	int& b = a;//&跟类型在一起的适合叫引用符
	cout << &a << endl << &b << endl;
	return 0;
}

结果:
在这里插入图片描述
引用在定义时必须初始化
在这里插入图片描述

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

int main()
{
	int a = 1;
	int& b = a;
	int c = 10;
	b = c;//这行代码是给c取了个名字叫b吗?还是把c的值赋给b
	cout << a << endl << b << endl;
	return 0;
}

显而易见b = c的意思是把c的值赋给b
在这里插入图片描述

引用做参数(输出型参数)
引用做参数(提高效率)(大对象/深拷贝类对象)
引用作为返回值
但如果返回的那个别名的值空间归还给操作系统了,就会越界访问,相当于野指针。

平常写的交换两个数,使用指针,还要解引用

void Swap(int* left, int *right)
{
	int tmp = *left;
	*left = *right;
	*right = tmp;
}

用引用作为参数

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

引用作为返回值:

没有被static修饰的n:
我们知道每个函数都有自己的函数栈帧,出了栈帧,就会把使用权还给操作系统,我们返回的是n的别名,那我们再去访问的时候,n的那块空间的使用权以及不归我们使用了,可能结果是对的。
被static修饰的n:
出了函数栈帧也不会销毁,因为n是在静态区创建的。

看看下面两段代码有什么区别

被static修饰的n

int& Count()
{
	static int n = 0;
	n++;
	return n;
}

没被static修饰的

int& Count()
{
	int n = 0;
	n++;
	return n;
}

总结:
基本任何场景都可以用引用传参
谨慎使用引用做返回值,除了函数作用域,对象不在了,就不能用引用返回,对象还在,就可以用引用返回

常引用

不同类型会产生临时变量(临时变量具有常性)
在这里插入图片描述

传值、传引用效率比较

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

在这里插入图片描述
返回值类型的性能比较

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

引用和指针的区别

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

2.auto关键字

auto自动推导类型
autu不能在同一行定义多个不同类型变量
auto不能作为函数参数
auto不能直接用来声明数组

在这里插入图片描述

使用场景

  1. 类型难于拼写
    2.含义不明确导致容易出错

当类型很长的时候,可以使用auto
在这里插入图片描述
auto与指针和引用结合起来使用
用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&

int main()
{
	int x = 10;
	auto a = &x;//把x的地址交给指针
	auto* b = &x;//
	auto& c = x;
	cout << typeid(a).name() << endl;
	cout << typeid(b).name() << endl;
	cout << typeid(c).name() << endl;
	*a = 20;
	*b = 30;
	 c = 40;
	return 0;
}

在同一行不能定义多个不同的变量

void TestAuto()
{
 	auto a = 1, b = 2; 
 	auto c = 3, d = 4.0; // 该行代码会编译失败,因为c和d的初始化表达式类型不同
}

3.范围for 语法糖

以前我们C语言要遍历数组是这样的:
在这里插入图片描述

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

  • 依次取arr数组中值赋给e
  • 自动迭代,自动判断结束
  • 适用于数组
void TestFor()
{
	int array[] = { 1, 2, 3, 4, 5 };
	for (auto& e : array)//如果要改变数组中的值,auto后面加&
		e *= 2;//每次对数组的值*=2

	for (auto e : array)
		cout << e << " ";//打印
}

4.inline函数

C语法:宏函数 Add(x, y) ((x)+(y))
优点:不需要建立栈帧,提高效率
缺点:复杂,容易出错,代码可读性变差

在这里插入图片描述

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

在函数前面加个inline就是内联函数

在这里插入图片描述

inline适用于短小的且频繁调用的函数,否则会造成代码膨胀,代码膨胀的后果就是可执行程序变大
inline对于编译器仅仅只是一个建议,最终是否成为inline,取决于编译器
类似下面的函数加了inline,也会被编译器否决掉
1.比较长的函数不行
2.递归函数也不能搞inline
默认debug模式下,inline不会起作用,否则就无法调试

5.指针空值nullptr

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

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

在C++98中,字面常量0既可以是一个整形数字,也可以是无类型的指针(void*)常量,但是编译器默认情况下将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转(void *)0

在这里插入图片描述

正常情况下,打印的应该是f(int)和f(int*),但是情况并不是这样:
在这里插入图片描述

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

在这里插入图片描述

所以在C++中,推荐使用nullptr

评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会敲代码的小张:)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值