C++解析(9):关于const和引用的疑问
0.目录
1.关于const的疑问
2.关于引用的疑问
- 2.1 引用与指针
- 2.2 从C++语言与C++编译器角度看引用
- 2.3 从工程项目开发看引用
3.小结
1.关于const的疑问
const什么时候为只读变量?什么时候是常量?
const常量的判别准则:
- 只有用字面量初始化的const常量才会进入符号表
- 使用其它变量初始化的const常量仍然是只读变量
- 被volatile修饰的const常量不会进入符号表
(volatile代表所声明的标识符是易变的,是可能发生变化的,只不过这个可能性也许不在当前编译的文件当中,这个使得当前标识符发生变化的可能性是发生在外部的,外部就相当于是其它文件当中,比如多线程、中断等等。volatile既然代表易变的,那么每次访问volatile所修饰的标识符时应该到内存里面去直接读取。这就意味着被volatile所修饰的标识符不可能进入符号表,也就是说当volatile和const同时修饰一个标识符的时候,得到的仅仅是一个只读变量,const在这里的意义仅仅是说明在当前的文件当中或者说当前的作用域当中,volatile和const同时修饰的标识符不能出现在赋值符号的左边。)
总结:在编译期间不能直接确定初始值的const标识符,都被作为只读变量处理。
const引用的类型与初始化变量的类型:
- 相同:初始化变量成为只读变量
- 不同:生成一个新的只读变量
示例程序:
#include <stdio.h>
int main()
{
const int x = 1;
const int& rx = x;
int& nrx = const_cast<int&>(rx);
nrx = 5;
printf("x = %d\n", x);
printf("rx = %d\n", rx);
printf("nrx = %d\n", nrx);
printf("&x = %p\n", &x);
printf("&rx = %p\n", &rx);
printf("&nrx = %p\n", &nrx);
return 0;
}
运行结果:
[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out
x = 1
rx = 5
nrx = 5
&x = 0x7ffd8a40b3cc
&rx = 0x7ffd8a40b3cc
&nrx = 0x7ffd8a40b3cc
使用volatile,是真正的常量吗?
证明在编译期间不能直接确定初始值的const标识符,都被作为只读变量处理。
#include <stdio.h>
int main()
{
volatile const int y = 2;
int* p = const_cast<int*>(&y);
*p = 6;
printf("y = %d\n", y);
printf("p = %p\n", p);
const int z = y;
p = const_cast<int*>(&z);
*p = 7;
printf("z = %d\n", z);
printf("p = %p\n", p);
return 0;
}
运行结果:
[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out
y = 6
p = 0x7fff2eab9144
z = 7
p = 0x7fff2eab9140
最后验证const引用的类型与初始化变量的类型相同和不同的情况:
#include <stdio.h>
int main()
{
char c = 'c';
char& rc = c;
const int& trc = c;
rc = 'a';
printf("c = %c\n", c);
printf("rc = %c\n", rc);
printf("trc = %c\n", trc);
return 0;
}
运行结果:
[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out
c = a
rc = a
trc = c
2.关于引用的疑问
2.1 引用与指针
引用与指针有什么关系?如何理解“引用的本质就是指针常量”?
指针是一个变量:
- 值为一个内存地址,不需要初始化,可以保存不同的地址
- 通过指针可以访问对应内存地址中的值
- 指针可以被const修饰成为常量或者只读变量
引用只是一个变量的新名字:
- 对引用的操作(赋值,取地址等)都会传递到代表的变量上
- const引用使其代表的变量具有只读属性
- 引用必须在定义时初始化,之后无法代表其它变量
2.2 从C++语言与C++编译器角度看引用
从使用C++语言的角度来看:
- 引用与指针没有任何的关系
- 引用是变量的新名字,操作引用就是操作对应的变量
从C++编译器的角度来看:
- 为了支持新概念“引用”必须要一个有效的解决方案
- 在编译器内部,使用指针常量来实现“引用”
- 因此“引用”在定义时必须初始化
2.3 从工程项目开发看引用
在工程项目开发中:
- 当进行C++编程时,直接站在使用的角度看待引用,与指针毫无关系,引用就是变量的别名
- 当对C++代码进行调试分析时,一些特殊情况,可以考虑站在C++编译器的角度看待引用
下面的代码有问题吗?
#include <stdio.h>
int a = 1;
struct SV
{
int& x;
int& y;
int& z;
};
int main()
{
int b = 2;
int* pc = new int(3);
SV sv = {a, b, *pc};
int& array[] = {a, b, *pc}; // &array[1] - &array[0] = ? Expected ==> 4
printf("&sv.x = %p\n", &sv.x);
printf("&sv.y = %p\n", &sv.y);
printf("&sv.z = %p\n", &sv.z);
delete pc;
return 0;
}
运行结果为:
[root@bogon Desktop]# g++ test.cpp
test.cpp: In function ‘int main()’:
test.cpp:17: error: declaration of ‘array’ as array of references
原因:引用数组破坏了数组地址空间的连续性!!!
C++不支持引用数组!C++不支持引用数组!C++不支持引用数组!
注释掉引用数组后:
#include <stdio.h>
int a = 1;
struct SV
{
int& x;
int& y;
int& z;
};
int main()
{
int b = 2;
int* pc = new int(3);
SV sv = {a, b, *pc};
printf("&sv.x = %p\n", &sv.x);
printf("&sv.y = %p\n", &sv.y);
printf("&sv.z = %p\n", &sv.z);
delete pc;
return 0;
}
运行结果为:
[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out
&sv.x = 0x600b14
&sv.y = 0x7ffc32798de4
&sv.z = 0x23be010
3.小结
- 指针是一个变量
- 引用是一个变量的新名字
- const引用能够生成新的只读变量
- 在编译器内部使用指针常量实现“引用”
- 编译时不能直接确定初始值的const标识符都是只读变量