欢迎大家的来到小鸥的博客,今天我们继续C++基础的第二篇吧!
这也是入门基础篇的最后一篇wo~
目录
1.引用
引用的概念
引用的特性及使用
const常引用
指针和引用的关系
2.inline内联函数
定义
相关特性及使用
3.nullptr
>>后记<<
1.引用
引用的概念
引用不是新定义⼀个变量,而是给已存在变量取了⼀个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同⼀块内存空间。
就像你同时拥有名字和外号一样,他们都代表了你这个个体;变量名和这个变量的引用,都同样代表了这个变量本身。
在类型名的后面加上&符号,就是一个这种类型的引用,在C语言中&加在变量名前面,意思为取地址;而在类型名后面加上&,则代表引用。
定义方式: 类型& 引用别名 = 引用对象;
引用的特性及使用
特性:
- C++中引用在定义时必须初始化,且引用一但指向一个实体,不能改变指向;
一个变量可以有多个引用,且引用不会另开空间;
引用不能代替指针,比如在链表中,就不能用引用代替指针(因为引用无法改变指向);
由于不可以改变指向,所以有以下情况:
int a = 10; int b = 20; int& d = a;//别名d指向a,且不可改变 d = b;//此时表示的就是一个赋值语句,将b的值赋值给d,d的指向仍然为变量a,即a=d=20,而不是将别名d的指向改为变量b
使用:
1.引用多用于传参和返回值:
- 当做参数时,形参就是一个别名,就相当于直接将实参本身给了函数,这样即使不进行传址调用,也同样可以影响到实参本身。
当作返回值时,返回的就是一个引用,可以对其进行修改,而正常情况下的返回值是存放在一个临时变量中的,临时变量具有常性,不可以修改。
2.当引用做返回值时,若返回一个局部变量,将导致出错,如:
int& Fun() {int a = 0;return a; }//变量a为局部变量,出函数后将会销毁
3.java 中的引用可以不初始化,且可以改变指向,更像 C/C++ 中的指针。
const常引用
const引用即常引用,其所取的别名,将使当前别名的权限为只读不写,const引用也可以对常量以及带有常性的数据(临时变量)进行取别名
临时对象:编译器需要⼀个空间暂存表达式的求值结果时临时创建的⼀个未命名的对象,C++中把这个未命名对象叫做临时对象。
const引用的使用牵扯到了权限的放大和缩小,只有指针和引用才会牵扯到权限的放大和缩小,在引用时,只能缩小权限,不能放大权限。代码演示(详细解释见注释处):
int main() {int a = 1;const int b = 2;double c = 1.1;const double d = 2.2;const int& ra = a;//权限的缩小//int& rb = b; //权限的放大,错误的//b本身就是一个const修饰的常变量,权限为只读不写,而int&的权限为可读可写,引用不可以放大权限//int& rc = c; //c为double类型的变量,需要隐式类型转换为int类型,//但隐式类型转换的结果会先存储到一个临时对象中,临时对象具有常性const int& rc = c;//所以加上const修饰使其成为常引用,就可以指向c了return 0; }
const修饰的引用还可以指向常量和表达式:
int a = 1;//int& c = 30; //(错误使用) const int& ra = 30;//常量只能用具有常性的const常引用;//int& rc = (a + 1);//(错误使用) const int& rb = (a + 1);//表达式的计算结果会先存储到一个临时对象中,临时对象用const常引用
指针和引用的关系
在实际运用中,指针和引用相辅相成,虽然功能有部分重叠,但又各有特点,互相不可替代。
- 在语法概念上引用是给一个变量取别名,不需要开辟空降;而指针则是一个新的变量用于存储地址,需要开辟空间;
- 引用必须初始化指向,而指针虽然建议初始化,但是非必须的;
- 引用不能后续改变指向,而指针可以;
- 引用可以直接访问变量,而指针要通过解引用来间接访问;
- 在sizeof操作符中,引用的大小为其引用的类型大小(如 sizeof(int),sizeof(char) 等),而sizeof(指针)的大小为地址空间所占的字节个数(32位4字节,64位8字节);
- 引用的使用更加安全,不易出错,而指针容易出现空指针和野指针的情况。
2.inline内联函数
定义
使用inline修饰的函数叫做内联函数,在编译时,C++编译器就会在调用内联函数的地方展开内联函数(效果类似宏函数),省去了建立栈帧的消耗,可以一定程度提高效率。
相关特性及使用
1.inline内联函数在Debug模式下默认是不展开的,这样方便调试,想要展开时,需要更改设置:
解决方案资源管理器窗口下,右击项目文件打开属性,根据图示进行修改:
2.nline内联函数对于编译器而言,是可选择执行的,当inline修饰的函数较为简单时,编译器才会展开,当函数较为复杂,展开消耗更大时,编译器就可以选择忽略inline,不对其进行展开;这是由编译器所决定的,不同的编译器标准不同。
inline int Fun() {int ret = 10;return ret; }//较为简短,inline生效int main() {int a = Fun();cout << a << endl;return 0; }
inline int Fun() {int ret = 10;ret += 1;ret += 1;ret += 1;ret += 1;ret += 1;ret += 1;ret += 1;ret += 1;ret += 1;return ret; }//函数较为复杂,inline不生效int main() {int a = Fun();cout << a << endl;return 0; }
此处的汇编call语句相当于一个跳转语句,意思为跳转到函数Fun,括号中为函数的地址。
3.inline的意义就是用于替代C语言中的宏函数,因为宏函数使用时较为复杂且容易出现错误;
以宏函数实现相加为例:
#define ADD(x,y) ((x)+(y)) //为什么要表达式外面要加括号 // 为什么x,y分别要加括号 // 为什么不加分号//错误示范 /* #define ADD(x,y) x + y #define ADD(x,y) (x + y); #define ADD(x,y) (x)+(y) ... */
而如果使用inline内联函数就可以实现替换需求的同时,实现函数一样的简单定义方式。
4.一般函数我们都是定义和声明分开的,但inline修饰的内联函数不建议声明和定义分离到两个⽂件,分离会导致链接错误。因为inline被展开,就没有函数地址,链接时会出现报错。
3.nullptr
在C语言中我们知道NULL,而NULL实际上就是一个宏,在传统的C头文件(stddef.h)中,我们可以看到如下代码:
#ifndef NULL#ifdef __cplusplus#define NULL 0#else#define NULL ((void *)0)#endif
#endif
由此可以看出在C++中NULL可能被定义为了字符常量0,而在C语言中则定义为了⽆类型指针(void*)的常量,但这两种定义在我们想使用空指针时都会出现一些麻烦和问题,如下代码:
void Fun(int x) {cout << "Fun(int x)" << endl; } void Fun(int* ptr) {cout << "Fun(int* ptr)" << endl; } int main() {Fun(0);Fun(NULL);//由于C++定义NULL为整型的0,所以和相当于Fun(0)Fun((int*)NULL);//Fun((void*)0);//在C语言中允许(void*)类型隐式转换为任意类型指针,但C++不行//C++void* ptr = NULL; //C++的类型转换更加严格int* pa = (int*)ptr;//C++中必须进行显示转换,将void*类型强转为int*类型//C/*void* ptr = NULL;int* pa = ptr;//C语言允许void*类型隐式转换为其他类型*/return 0; }
结果如下:
这样Fun(NULL)语句就和我们本来想要达到的目的相悖了。
所以在C++中就定义了一个nullptr,nullptr是一个特殊的关键字,nullptr是⼀种特殊类型的字⾯量,它可以转换成任意其他类型的指针类型,这样可以防止想要使用NULL时被转换为整型0的问题,因为nullptr可以被隐式转换为任意类型的指针,而不是整型。
使用nullptr代替NULL即可使Fun(nullptr)正确的指向第二个函数。
>>后记<<
本篇讲解了引用,inline内联函数,nullptr关键字的相关定义和作用,有不足的地方大家可以在评论区或私信指出,大家互相学习wa
个人主页:海盗猫鸥-CSDN博客
本篇专栏:C++_海盗猫鸥的博客-CSDN博客
再次欢迎大家的到访,那么本篇就到这里,我们下篇再见——