type
status
date
slug
summary
tags
category
icon
password
Property
引用和指针都是复合类型,复合类型是指基于其他类型定义的类型
引用
C++11
新增了右值引用,一般的引用指的是左值引用。引用为对象起了另外一个名字,语法格式:通过将声明符写成
&d
的形式来定义引用类型,其中d
是声明的变量名。不能定义引用类型的引用,但可以定义任何其他类型的引用。一般在初始化变量时,初始值会被拷贝到新建的对象中。然而定义引用时,程序把引用和它的初始值绑定在一起,而不是将初始值拷贝给引用。一旦初始化完成,将无法再令引用重新绑定到另一个对象,因此引用必须初始化。
定义了一个引用之后,对其进行的所有操作都是在与之绑定的对象上进行的:
为引用赋值, 实际上是把值赋给了与引用绑定的对象;获取引用的值,实际上是获取了与引用绑定的对像的值;同理,以引用作为初始值, 实际上是以与引用绑定的对象作为初始值。
引用不是对象,它只是为一个已经存在的对象所起的另外一个名字,所以不能定义引用的引用。
声明语句中引用的类型实际上被用于指定它所绑定的对象类型。大部分情况下,引用的类型要和与之绑定的对象严格匹配。引用只能绑定在对象上,不能与字面值或某个表达式的计算结果绑定在一起:
可以在一个类型定义行中定义多个引用,必须在每个引用标识符前添加
&
符号:指针
与引用类似,指针也实现了对其他对象的间接访问,但指针与引用相比又有很多不同点:
- 指针本身就是一个对象,允许对指针赋值和拷贝,而且在生命周期内它可以先后指向不同的对象
- 指针不需要在定义时赋初值。和其他内置类型一样,在块作用域内定义的指针如果没有被初始化,也将拥有一个不确定的值
从根本上看,指针是一个值为内存地址的变量(或数据对象)。正如
int
类型变量的值是整数,指针变量的值是地址。通过将声明符写成
*d
的形式来定义指针类型,其中d
是变量名称。如果在一条语句中定义了多个指针变量,则每个量前都必须有符号*
。获取对象的地址
指针存放某个对象的地址,要想获取对象的地址,需要使用取地址符:
把
p
定义为一个指向int
类型的指针,随后初始化p
令其指向名为i
的int
对象。因为引用不是对象,没有实际地址,所以不能定义指向引用的指针。声明语句中指针的类型实际上被用于指定它所指向的对象类型。大部分情况下,指针的类型要和它指向的对象严格匹配。
利用指针访问对象
如果指针指向一个对象,可以使用解引用(dereference)符
*
来访问该对象:给解引用的结果赋值,就是给指针所指向的对象赋值:
解引用操作仅适用于那些确实指向了某个对象的有效指针。
某些符号有多重含义
像
&
及和*
这样的符号, 既能用作表达式里的运算符,也能作为声明的一部分出现,符号的上下文决定了符号的意义:指针值
指针的值(即地址)应属于下列状态之一:
- 指向一个对象
- 指向紧邻对象所占空间的下一个位置
- 空指针,即指针没有指向任何对象
空指针不指向任何对象,在试图使用一个指针前代码可以先检查它是否为空。得到空指针最直接的办法是用字面值
nullptr
来初始化指针:旧版本通常使用
NULL
(预处理变量,定义于头文件cstdlib
中,值为0)给指针赋值,但在C++11
中,最好使用nullptr
初始化空指针。把
int
变量直接赋给指针是错误的操作, 即使int
变量的值恰好等于0也不行:建议初始化所有指针:使用未经初始化的指针是引发运行时错误的一大原因。如果不清楚指针应指向何处, 就把它初始化为
nullptr
或者0。- 无效指针,即上述情况之外的其他值
试图拷贝或以其他方式访问无效指针的值都会引发错误。编译器并不负责检查此类错误,这一点和试图使用未经初始化的变量是一样的,访问无效指针的后果无法预计,因此需要清楚指针是否有效。
赋值和指针
指针和引用都能提供对其他对象的间接访问,然而在具体实现细节上二者有很大不同:
- 引用本身并非一个对象。一旦定义了引用,就无法令其再绑定到另外的对象,之后每次使用这个引用都是访问它最初绑定的那个对象
- 指针和它存放的地址之间就没有这种限制了。和其他任何变量(只要不是引用) 一样,给指针赋值就是令它存放一个新的地址,从而指向一个新的对象。
赋值永远改变的是等号左侧的对象:
指针与判断
有效的指针值可以用于条件判断,空指针被判断为
false
,任何其它值被判断为true
。给定两个相同类型的有效指针,如果它们指向同一个地址,那么它们就被认为是相同的,==
将返回true
。两个同时为空指针的指针也被认为是相同的。由于条件判断或者比较操作用到了指针的值,因而,无效指针将导致以上行为未定义。void*指针
void*
是一种特殊的指针类型,可以存放任意对象的地址,但不能直接操作void*
指针所指的对象:void*
指针只能做几种操作:与其它指针比较、传递给函数或者从函数中返回,赋值给其它的 void*
指针。void*
指针不能解引用,因而也不能对操作其指向的对象,因为不知道对象的类型,也不知道对象拥有的操作。要对指向的对象进行操作,必须将指针的类型进行强转。void*
指针的作用在于引用一块内存,而不是使用指针来访问指向的对象。复合类型的声明
变量包括一个基本数据类型(base type)和一组声明符。
当在一个声明语句中声明多个变量时,每个声明符都说明它对应的变量与基础类型的关系,这与别的声明符是独立的,因而,一个声明语句中可以定义多个不同类型的变量:
类型修饰只修饰自己,不是这条语句中所有变量。
指向指针的指针
C++
对于声明符中修饰符的个数并没有限制。以指针为例, 指针是内存中的对象, 像其他对象一样也有自己的地址, 因此允许把指针的地址再存放到另一个指针当中。使用
**
表示指针的指针,***
表示指针的指针的指针:指向指针的引用
引用本身不是一个对象,因此不能定义指向引用的指针。但指针是对象,所以存在对指针的引用:
离变量名最近的符号(此例中是
&r
的符号&
)对变量的类型有最直接的影响,因此r
是一个引用。声明符的其余部分用以确定r
引用的类型是什么,此例中的符号*
说明r
引用的是一个指针。最后,声明的基本数据类型部分出 r
引用的是一个 int
指针。