1. 指针(pointer)
1.1 定义
指针(pointer)本质上是指向(point to)其他对象的对象,其内存中存储的是地址,可以通过解引用操作间接的去访问其指向内存对象(如果指针中存储的是一个有效的地址)
1.2 初始化和赋值
指针无需在定义时初始化,并且可以随时指向其他对象,因此在使用指针前,需要对其进行有效性检查,否则可能会访问无效指针
1.3 指针的值
指针的值有四种可能
1. 指向一个对象
2. 指向紧邻对象所占空间的下一个位置
3. 空指针,即没有指向任何对象
4. 无效指针
1.4 void*
void* 指针可以指向任意类型的对象,但不能直接操作其指向的对象
1.5 const和指针
- 指针常量(T* const p)是一个指针类型的常量,在定义时必须初始化,并且该指针在初始化指向某一对象后不能在指向其他对象了,但可以通过指针去改变其所指向的对象的值
- 常量指针(const T* p)是一个指向“常量对象”的指针,即不能通过该指针对改变其指向对象的值,但该指针可以指向其他对象。可以让一个常量指针指向一个非常量对象,但常量对象只能由常量对象指向
1.6 类型匹配
指针的类型必须与其所指向对象的类型严格匹配,但有两个例外:
- 常量指针(const T*)可以用任意表达式进行赋值,只要改表达式的旳值能转化成该指针的类型(T)即可,如非常量对象、字面值
int i = 24;
const int* p1 = i; // ok
const int* p2 = 0x24; // ok 指向一个地址为0x24的int型临时对象上
int* p3 = 24 // error
const char* p4 = "hello world" // ok 指向一个内容为"hello world"的char* 型临时对象上
char* p4 = "hello world" // error
- 可以让一个基类指针指向派生类对象
class Base
{
...
};
class Derived : pulic Base
{
...
}
Base* p = new Derived();
2 引用(reference)
2.1 定义
引用(reference)是其所绑定对象的别名,操作引用相当于直接操作其引用的对象
2.2 初始化和赋值
引用在定义时必须初始化,即将该引用与一个对象绑定,并且该引用在其生命周期内不能在绑定其它对象
2.3 const和引用
- 由于引用在其生命周期内只能绑定一个对象,所以其天然具有常量性,所以也没有引用常量这个说法
- 常量引用(const T& )是一个对“常量对象”的引用,即不能通过该引用去修改其引用对象的值
2.4 类型匹配
引用的类型必须与其所引用对象的类型严格匹配,但有两个例外:
- 常量引用(const T&)初始化时可以用任意表达式作为初始值,只要改表达式的旳值能转化成该引用的类型(T)即可,如非常量对象、字面值
int i = 24;
const int& r1 = i; // ok
const int& r2 = 24; // ok 绑定到一个值为24的int型临时对象上
int& r3 = 24 // error
double d = 24.0;
const int& r4 = d // ok 绑定到一个值为24的int型临时对象上
const int& r5 = 24.0 // ok 绑定到一个值为24的int型临时对象上
int& r5 = d // error
- 可以将一个派生类的对象绑定到一个基类引用上
class Base
{
...
};
class Derived : pulic Base
{
...
}
Derived derived;
Base& p = derived;
3. 区别
- 引用一个对象的别名;指针是存有其他对象地址的对象
- 引用在定义时必须初始化;指针不必
- 引用在初始化时绑定一个对象后,在其生命周期内不可以再绑定到其它的对象;指针在其生命周期内可以任意修改其值
- 在使用引用前不用对其进行有效性检查;指针必须对进行有效性检查
- 不存在引用的引用,即多级引用;存在指针的指针,即多级指针
- 不存在引用常量;存在指针常量
- 引用和指针的算术运算的意义不同
4. 引用的底层实现
C++标准中没有规定引用是如何实现的,这个工作交给了编译器,然而大多数编译器(VS和GCC)使用了指针常量去实现引用,即引用是指针的语法糖(syntactic sugar),只不过对使用者隐藏了实现细节
The C++ standard is very careful to avoid dictating how a compiler must implement references, but every C++ compiler implements references as pointers. That is, a declaration such as:
int &ri = i;
if it’s not optimized away entirely, allocates the same amount of storage as a pointer, and places the address of i into that storage.
int i = 24;
int &r = i;
等价于
int* const r = &i
r = 25;
等价于
*r = 25