C++ 中引用与 const 引用

内容转载自:https://www.cnblogs.com/chio/archive/2007/07/18/822362.html 按照自己的理解略有修改

(1) 在实际的程序中,引用主要被用做函数的形式参数-通常将类对象传递给一个函数

引用必须初始化,但是用对象的地址值初始化引用是错误的,为了引用对象的地址,可以首先定义一个指针,再定义一个指针的引用:

int ival = 1092;
int &ri = ival; // ok
//int &rp = &ival; // 错误
int *p = &ival;
int *&rp = pi; // ok,一个指针的引用

(2) 一旦引用已经定义,就不能再指向其他的对象,这就是为什么要对引用初始化的原因

int *pi = &ival;
int *&rp = pi; // ok
int *pi1 = &ival;
// 会报错:error: redeclaration of ‘int*& rp’
int *&rp = pi1;

(3) const 引用可以用不同类型的对象初始化(只要能从一种类型转换到另一种类型即可),也可以是不可寻址的值,如文字常量。例如

double dval = 3.14159;
// 下面3行仅对const引用才是合法的
const int &ri = 1024;
const int &ri1 = dval;
const double &rd = dval + 1.0;

// 补充说明:
int const &r = a;
// 等价于
const int &r = a;

上面,同样的初始化对于非 const 引用是不合法的,将导致编译错误,原因是引用在内部存放的是一个对象的地址,它是该对象的别名。对于不可寻址的值(文字常量和地址值)以及其他类型的对象,编译器为了实现引用,必须生成一个临时对象,引用实际上指向该对象,但用户不能访问它。

例如:

double dval = 23;
const int &ri = dval;

编译器将其转换为:

int tmp = dval; // double -> int
const int &ri = tmp;

同理:

double dval = 3.14159;

// 不可寻址,文字常量
int tmp1 = 1024;
const int &ir = tmp1;

// 不同类型
int tmp2 = dval;//double -> int
const int &ir2 = tmp2;

// 不可寻址的另一种情况
double tmp3 = dval + 1.0;
const double &dr = tmp3;

(4) 不允许非 const 引用指向需要临时对象的对象或值,即,编译器产生临时变量的时候引用必须为 const

int ival = 100;
int *&rp = &ival; // 错误,非const引用对需要临时对象的引用
int *const &rp = &ival; // ok

const int ival = 1024;
int *&rp = &ival;    // 错误,非const引用是非法的
const int *&rp = &ival;   // 错误,地址值的引用不可寻址所以需要临时变量,需要将引用声明为const
const int *const &rp = &ival;  // 正确

// 补充
const int *p = &ival;
const int *&rp = p;  //正确, 因为 p 是指针不再是地址值,可以寻址

对于

const int *const &rp = &ival;

的更进一步解释:

  1. 不允许非 const 引用指向需要临时对象的对象或值
    int a = 2; 
    int &ref1 = a;// ok, 不存在临时变量 
    const int &ref2 = 2;// ok, 因为文字常量 2 不可寻址,编译器自动产生临时变量,需要 const 引用
    
  2. 地址值是不可寻址的值, 所以应该用 const 引用
    int* const &ref3 = &a;   // ok
    
  3. 于是,用一个指向 const int 的指针的 const 引用才能进行初始化
    const int ival = 1024;
    const int *p = &ival; 
    const int *&rp = p;   // ok
    const int *const &rp1 = &ival;   // ok
    

(5) const 引用的语义

const 引用表示,试图通过此引用去(间接)改变其引用的对象的值时,编译器会报错,但是这并意味着,此引用所引用的对象也因此变成 const 类型了。我们仍然可以改变其指向对象的值,只是不通过引用

 1 #include <iostream>
 2 using namespace std;
 3 
 4 int main()
 5 {
 6     int ival = 1024;
 7     const int &ri = val;
 8     
 9     val++;
10     //ri++;
11 
12     cout << val << " " << ir << endl;
13 
14     return 0;
15 }

其中第10行,如果我们通过 ir 来改变 ival 的值,编译时会出错。但是我们仍然可以通过 ival 直接改变其值(第9行)。