C++入门基础(三):const引用、指针和引用的关系、inline(修饰内联函数)替代宏、nullptr代替null
🔥个人主页:胡萝卜3.0
🎬作者简介:C++研发方向学习者
📖个人专栏: 《C语言》《数据结构》 《C++干货分享》
⭐️人生格言:不试试怎么知道自己行不行
前言:C++入门基础(二)中介绍了C++中的三个重要特性:缺省参数、函数重载和引用机制。1. 缺省参数分为全缺省和半缺省,半缺省必须从右向左连续定义,且只能在函数声明中指定缺省值;2. 函数重载允许同名函数通过参数个数、类型或顺序不同而区分,但不支持仅返回值不同的重载;3. 引用是变量的别名,不占用额外内存,常用于函数参数传递以替代指针,但链表等数据结构仍需使用指针。文章还通过实例分析了引用作为形参和返回值的正确使用方法,指出局部对象不宜作为引用返回,并揭示了引用在底层仍通过指针实现的本质。接下我们继续学习相关入门知识。
目录
一、const引用
1.1 权限放大
1.2 权限缩小
1.3 权限平移
二、指针和引用的关系
三、inline(修饰内联函数)替代宏
3.1 回顾宏
3.2 宏是一把双刃剑
3.3 内联函数的书写
3.4 inline不展开--编译器的王道
3.5 inline不建议声明和定义分离到两个文件
四、nullptr代替null
一、const引用
可以引用一个const对象,但是必须用const引用。const引用也可以引用普通对象,因为对象的访
问权限在引用过程中只可以缩小,但是不能放大。
权限在引用过程中只可以缩小,但是不能放大?这是什么意思?接下来,我们来看看什么是权限的缩小和放大:
1.1 权限放大
1.2 权限缩小
注意:权限的放大和缩小,只存在const 修饰指针,&引用的场景
1.3 权限平移
const 引用 可以引用常量
int main() {const int& a = 10;return 0; }
a是数字10的别名,但是不能通过修改a,来修改10。
通过前面的学习,我们知道引用可以作形参,当引用作形参的时候,建议按照下面的写法:
学习了上面的几种场景,接下来看看这种场景
上图中红方框内的一行代码为啥会报错呢?
其实在这里我们就要学习一个东西叫做:隐式类型转换,那何为隐式类型转换呢?通过下面一张图,我们就能很清晰的了解什么是隐式类型转换:
注意:隐式类型转换(C语言支持),还有显式类型转换、强制类型转换。
整型---整型,整型---浮点型,浮点型---浮点型,但是类型之间的转换一定要有关联。
那既然知道了什么是隐式类型转换 ,那我们来看看上图中红方框内的一行代码为啥会报错:
所谓临时对象就是编译器需要⼀个空间暂存表达式的求值结果时临时创建的⼀个未命名的对像,C++中把这个未命名对象叫做临时对象。
注意:传值返回时,不是直接将值返回,而是:如果这个值较小,它会先放在寄存器中,然后通过寄存器返回,如果较大,它会在开辟一个新的空间用来存储该返回值。
二、指针和引用的关系
C++中指针和引用就像两个性格迥异的亲兄弟,指针是哥哥,引用是弟弟,在实践中他们相辅相成,功能有重叠性,但是各有自己的特点,互相不可替代。
- 语法概念上引用是⼀个变量的取别名不开空间,指针是存储⼀个变量地址,要开空间。
- 引用在定义时必须初始化,指针建议初始化,但是语法上不是必须的。
- 引用在初始化时引用⼀个对象后,就不能再引用其他对象;而指针可以在不断地改变指向对象。
- 引用可以直接访问指向对象,指针需要解引用才是访问指向对象。
- sizeof中含义不同,引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节,64位下是8byte)
- 指针很容易出现空指针和野指针的问题,引用很少出现,引用使用起来相对更安全⼀些。
上面指针和引用的关系还是比较重要的,也许在今后的面试中会被提问到,所以我们需要对这些知识进行理解,而不是死记硬背,当然,如果在面试的时候,漏了其中的一两点也没啥太大的问题。
三、inline(修饰内联函数)替代宏
3.1 回顾宏
在C语言的学习中,我们学习了宏相关的知识:
- 宏是一种替换机制
- 宏的后面不需要加上 ‘;’
- 宏函数的重点不在后面的函数。
那这里就会有小伙伴感到疑惑了,既然我们已经学习了宏,那为什么还要学习inline?接下来我们来看一下:
3.2 宏是一把双刃剑
宏很坑,宏函数很容易失控,容易写出问题,并且不能调试,所以C++建议用const、enum、inline来替代宏
补充:
#define ADD(a, b) ((a) + (b));
int main()
{int ret1 = ADD(1, 2); // int ret1 = 1+2;;cout << ret1 << endl;return 0;
}
1、宏的后面为什么不能加上“;”
我们知道宏是一种替换机制,并且在预处理阶段就已经被替换了
2、 为什么我们在写宏的时候要用小括号分别将a和b括起来:
错误代码:
那既然宏有这么多缺点,那为啥还要在C语言期间学习宏相关的知识呢?这是因为宏也是有优点的:宏可以提高效率,在预处理阶段宏会被替换,并且不建立桟帧
ok,接下来我们来看一下内联函数该如何写:
3.3 内联函数的书写
假如现在我们想书写一个Add函数:
int Add(int x,int y)
{return x+y;
}
Add的内联函数就是在函数的前面加上inline
inline int Add(int x,int y)
{return x+y;
}
编译时C++编译器会在调用的地方展开内联函数,这样调用内联函数就不需要建立栈帧了,就可以提高效率,并且内联函数可以调试。
3.4 inline不展开--编译器的王道
inline不展开?这是什么原因?
inline对于编译器而言只是⼀个建议,也就是说,你加了inline编译器也可以选择在调用的地方不展开,不同编译器关于inline什么情况展开各不相同,因为C++标准没有规定这个。inline适用于频繁调用的短小函数,对于递归函数,代码相对多⼀些的函数,加上inline也会被编译器忽略。(inline被忽略是指该创建函数桟帧的创建函数桟帧,不展开)
那怎么知道inline函数是展开还是不展开呢?
可以通过汇编观察程序是否展开,有call Add语句就是没有展开,没有call Add语句就是展开了
补充:
(1)编译器默认debug版本下,为了方便调试,inline也不展开(我们去反汇编看)
(2)release版本会展开,但是release版本一不能调试,二不能看汇编。
下面是我们在观察inline修饰内联函数是否展开时对VS编译器的修改——
为啥inline对于编译器而言只是一个建议?
如果交给程序员自己,可能会导致代码指令膨胀,导致安装包过大
比如:inline func函数,展开后是20行指令,现在又10000个调用地方
展开:20*10000行指令 不展开:20+10000行指令
3.5 inline不建议声明和定义分离到两个文件
inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地
址,链接时会出现报错。inline直接在.h文件中定义
四、nullptr代替null
NULL实际是⼀个宏,在传统的C头文件(stddef.h)中,可以看到如下代码:
- C++中NULL可能被定义为字面常量0,或者C中被定义为无类型指针(void*)的常量。不论采取何种定义,在使用空值的指针时,都不可避免的会遇到⼀些麻烦,本想通过f(NULL)调⽤指针版本的f(int*)函数,但是由于NULL被定义成0,调⽤了f(int x),因此与程序的初衷相悖。f((void*)NULL);调用会报错。
- C++11中引入nullptr,nullptr是⼀个特殊的关键字,nullptr是⼀种特殊类型的字⾯量,它可以转换成任意其他类型的指针类型。使用nullptr定义空指针可以避免类型转换的问题,因为nullptr只能被隐式地转换为指针类型,而不能被转换为整数类型。
重点:在C++中的空指针使用 nullptr代替null