复合类型是指基于其他类型定义的类型。 c++语言有几种复合类型, 这里将介绍其中的两种:引用和指针。
引用
·
·
·
·
基础引用
在c++11版本中引入了“右值引用”, 关于这个我们在这里先不讲解。 这种引用主要作用于内置类型。 严格的来说, 我们使用术语“引用”的时候, 默认的就是左值引用。
引用就像是为对象起了另外一个名字, 引用类型引用另外一种类型。 我们通过将声明写成&d的形式来定义引用, 其中d是声明的变量名:
int ival = 1024; //定义一个int类型变量
int &refVal = ival; // int类型refVal变量是对ival的一个引用
int &refVal2; //错误! 引用在定义的时候必须被初始化。
·
·
引用 == 别名
一般在初始化变量的时候, 初始值会被拷贝到新建的对象之中。 然而定义引用的时候, 程序吧引用和它的初始值绑定在一起, 而不是将初始值拷贝给引用。 一旦初始化完成, 引用将和他的初始值对象一直绑定在一起。 因为无法令引用重新绑定另外一个对象, 因此引用必须初始化。
定义了一个引用之后, 对其(这里指引用)所有的操作都是在与之绑定的对象上进行的。
int ival = 1024; //定义一个int类型变量
int &refVal = ival; // int类型refVal变量是对ival的一个引用
refVal = 2; //等价于ival = 2, 把2赋值给了引用绑定的对象;
int ii = refVal; //与ii = ival的调用效果一样
为引用赋值, 实际上是八值赋给了与引用绑定的对象, 获取引用的字, 实际上是获取与引用绑定的对象的值。 同理, 以引用作为初始值, 实际上是与引用绑定的对象作为初始值。
int ival = 1024; //定义一个int类型变量
int &refVal = ival; // int类型refVal变量是对ival的一个引用
int &refVal3 = refVal; // 正确! 这里rafVal3是inval的引用!!
int i = refVal; // 正确i被初始化为ival的值;
在这里我们要注意, 引用本身不是一个对象, 所以我们不能定义引用的引用。
上个代码中int &refVal3 = refVal;
实际上是在定义int &fefVal = ival
·
·
引用的定义
允许值啊一条语句之中定义多个引用, 其中每个引用都必须以符号&开头;
int i = 1024, i2 = 2048; //这里定义两个int类型的变量i、i2
int &r = i, r2 = i2; //这里r是一个引用, 与i绑定在一起, r2是int类型
int i3 = 1024, &r1 = i3; // i3是int类型, ri是一个引用, 与i3绑定在一起。
int &r3 = i3, &r4 = i2; //r3 和r4 都是引用
除了我们接下来将要介绍的两种情况之外, 其他所有的有引用的类型都要和与之绑定的对象严格匹配。并且引用只能绑定在对象上, 而不能与字面值或者某个表达式计算结果绑定在一起!!
int &refVal4 = 1024 //错误!只能绑定在对象上
double dval = 3.14;
int &refVal5 = dval //错误!此处引用的类型和初始值的类型必须相同
·
·
·
·
const的引用
我们可以把引用绑定到const上, 就像绑定到其他对象上, 我们称之为对常量的绑定。 与普通引用不同的是, 对常量的引用不能被用作修改它所绑定的对象。
const int a = 1024; //定义一个int 类型的常量 a
const int &b = a; //定义一个常量引用b 与a绑定
a = 42; // 错误!!!! a是常量不能改变他的值
int &d = a; // 错误 常量只能被常量引用所引用
因为a是常量, 我们不允许直接去为a赋值, 当然也就不能通过引用去改变a的值。 因此, 对d的初始化是错误的。 假设该初始化合法, 则可以通过const来改变他所引用的对象的值, 这显然是不合法的。
这里多说两句
const int &b = a;
中的const属于底层const 也就是对b的限定: 通过b修改值是不合法的这里我们用指针来举例:
int a;
int const *b = a 这里指b的指向不会发生改变, 只能指向a
const int *c = a 这里指不能通过c去修改a的值
·
·
初始化和对const的引用
在上文中我们提到, 引用的类型必须与其引用对象的类型一致, 但是有两个例外。 第一种意外情况就是在初始化常量引用的时候允许任意表达式作为初始值, 只要该表达式可以转换为引用的类型即可(也就是在引用的时候发生了强制类型转换); 另一种情况是, 常量引用绑定一个非常量的对象、字面值甚至是一个表达式。注意这两种特殊情况都是常量引用下的特殊, 也就是在基础引用的情况下是不存在特殊情况的
偷个懒直接上图:
想要理解这种例外的真正原因, 最简单的方法是弄清楚一个常量引用是如何被绑定到另外一个类型上的。
·
·
此次, ri引用了一个int类型的数。 对ri的操作应该是整数运算, 但那时dval却是一个双精度浮点数而非整数。因此为了确保让ri绑定一个整数, 编译器把上述代码转变成了下文的形式:
在这种情况下, ri绑定了一个临时量的对象, 所谓临时量的对象就是编译器需要一个空间来暂存表达式的求值的结果所临时创建的一个未命名的对象。 临时变量对象通常被简称为临时量。
接下来探讨ri不是常量时, 如果执行了类似于上面初始化的过程将带来什么。
如果ri不是常量, 如果执行了类似上面的操作, 就允许对ri赋值, 这样就会改变ri所引用的对象的值。 注意, 此时绑定的对象是一个临时变量而不是dval。 程序既让想让ri引用dval, 就肯定想通过ri改变dval的值, 否则ri的存在将无意义, 如此看来既然大家基本不会想把引用绑定到临时变量上, c++也就将这种行为归为非法。
·
·
对const的引用可以引用一个并非const的变量
必须认识到, 常量引用仅仅对引用课参与的操作进行了限定, 对于引用的对象本身是不是一个常量为未出限定。 因为对象也可以是一个非常量, 所以允许通过其他途径改变他的值;
r2非常量绑定整数i是合法行为。 然而, 不允许通过r2修改i的值。 尽管如此, 我们依然可以通过其他途径来修改i的值。 既可以直接给i赋值, 也可以通过像人一样绑定到i的其他引用来修改值。
c++中引用的实现本质是地址的传递