本篇内容主要是分享指针和引用、c++类型转换操作符等内容,我主要是也根据《More Effective C++》、《C++ primer》和一些面经进行总结的。
引用(reference)
定义:为另一个变量起了一个另外的名字(可以直接说变量的别名),通过声明符写成&refival的形式来定义引用类型,其中ival为声明的变量名。
对于变量别名的理解
引用变量和被引用变量并没有共用一块内存,引用变量其实是另外开辟了一块内存;
引用变量开辟的内存里面存放的是被引用变量的地址;
任何对引用变量的操作都会转换为对解引用的操作,例如:(*refval)代表的就是ival。
引用的注意事项
没有空引用,即定义必须初始化;
引用总是指向它最初获得的那个对象,即引用将和它初始值对象一直绑定在一起,无法令引用再绑定其他对象。
常量引用注意:
非常量引用不能绑定在常量上
常量引用既可以绑定在常量上也可以绑定在非常量上
不能通过常量引用去改变被引用的值
引用分类
左值:有名字,可以取地址,非临时
右值:没有名字,不能取地址,临时的(定义方式:类型 && 引用名 = 右值表达式)
本篇重点讲一下右值引用,因为这在面试中会经常被问到,比如说c++11特性、右值引用的特点。。。
右值引用
定义:指代临时的对象,它们只在当前的语句中有效。
目的:1、消除两个对象之间不必要的拷贝,节省运算存储资源,提高运行效率(转移语义);2、能够更简洁明确的定义泛型编程(完美转发)。
转移语义:将资源 ( 堆,系统对象等 ) 从一个对象转移到另一个对象,这样能够减少不必要的临时对象的创建、拷贝以及销毁,能够大幅度提高 C++ 应用程序的性能。临时对象的维护 ( 创建和销毁 ) 对性能有严重影响。
完美转发:在参数传递过程中属性和参数值都不变。
std::move:将左值引用转换为右值引用,实现转移语义。原理:折叠引用。
接下来就是分析指针和引用的区别。相信学习过c语言的人对指针并不陌生,其实指针就是一种保存变量地址的变量。因为对于每个变量来说都有自己的地址,而指针就是保存这个地址的变量。
具体指针和引用区别如下,这里面我只是举了一些常见的内容,还有一些可以自行去网上百度,很多讲的特别详细。
引用不需要解引用就可以直接获取指向内存空间的值,而指针需要解引用;
引用的赋值操作不需要取地址符来赋值,而是通过变量名;但是指针必须通过取地址符来实现;
引用必须要初始化,且指向的地址不能被改变;但是指针就更加灵活,可以初始化为空或者不初始化;
通过sizeof获取大小不同,指针一般和操作系统位数有关,如32位的指针占4个字节,但是引用是引用对象所占的大小;
引用和指针自增结果不同;
引用的底层是通过指针实现的;
通过malloc/new申请内存的返回值是指针。
C++强制类型转换
下面说这一段其实主要是为了引出四种类型操作符,可看可不看,不过z在面试中可以简单介绍一下为什么会出现这四种类型操作符。
对于C语言来说,它可能会隐式将一种类型转换成其他类型(比如说使用malloc分配内存时,其首先返回void*,然后隐式转换成相应类型的指针),但是这种转换并不能够精确的指明意图。虽然其也提供了一些方式来进行显示的转换,比如说使用一对小括号加上一个对象名称(标志符)组成,如
float fnum = 1.2;
int num = (int)fnum; //从float转换成int
由于小括号和对象名称在C++中任何地方都可能被用到,因此为了解决这些问题,C++引入了4种新的类型转换操作符,如下图所示。
static_cast
格式:static_cast
int firstNumber, secondNumber;
...
double result = static_cast
(firstNumber/ secondNumber)
用途
用于基本数据类型之间的转换,如把int转换成double
用于类类型转换,如派生类和基类之间,但是需要注意这种没有类型安全检查,即基类转换成派生类可能会有问题。
把空指针转换成目标类型的指针
注意
static_cast基本上拥有C就是转型相同的威力和意义,以及相同的限制。例如,不能够利用static_cast将一个struct转换成int,或将一个double转换成指针;另外,static_cast也不能去除表达式的常量性。因为有一个新式转型操作符const_cast专司此职。
《More Effective C++》
为什么要用static_cast转换而不用c语言中的转换?
更安全;
更直接明显,能够一眼看出是什么类型转换成什么类型,容易找出程序中的错误;
可清楚地辨别代码中每个显式的强制转换;可读性更好,容易被解析,且能体现程序员的意图。(可看《More Effective c++》第二节)
const_cast
格式:const_cast
用途
用来改变表达式中的常量性或易变性,即将某个对象的常量性去除掉
注意:const_cast不是用于去除变量的常量性,而是去除指向常数对象的指针或引用的常量性,其去除常量性的对象必须为指针或引用。
reinterpret_cast
格式:reinterpret_cast
用途:改变指针或引用的类型、将指针或引用转换为一个足够长度的整形、将整型转换为指针或引用类型。不过最常用的是转换“函数指针”类型。
注意:使用reinterpret_cast强制转换过程仅仅是比特位的拷贝,因此需要特别谨慎
dynamic_cast
格式:dynamic_cast
用途
其他三种类型转换都是编译时完成的,dynamic_cast是运行时处理的,运行时要进行类型检查;
不能用于内置的基本数据类型的强制转换;
dynamic_cast转换如果成功的话返回的是指向类的指针或引用,转换失败的话则会返回NULL或者抛出异常;
使用dynamic_cast进行转换的,基类中一定要有虚函数,否则编译不通过,但是static_cast没有这方面要求。
需要检测虚函数的原因:1、类中存在虚函数,就说明它有想要让基类指针或引用指向派生类对象的情况,此时转换才有意义;2、运行时类型检查需要运行时类型信息存储在类的虚函数表中,只有定义了虚函数的类才有虚函数表。
总结:对于引用部分,相信看面经的人知道问的几率挺多的,特别是右值引用。在回答的时候可以说一下右值引用和左值引用的区别、右值引用的作用、右值引用中转移语义和完美转发实现原理(其实就是折叠引用),然后据一些例子;另外,对于指针和引用的区别,我觉得可以先说引用为什么是变量的别名,接下来引出引用为什么必须要初始化,以及与指针的其他区别;最后,对于c++类型转换部分,可以分成编译时处理和运行时完成两类来讲解这四种类型转换,同时也要记得突出每种转换操作符的重点以及局限性,比如说const_cast主要去除常量性等等。以上只是本人的一些思路,具体还是要看个人情况而定。