注:博客中内容主要来自《狄泰软件学院》,博客仅当私人笔记使用。
测试环境:Ubuntu 10.10
GCC版本:4.4.5
一、关于const的疑问
const什么时候为只读变量?什么时候是常量?
1)const常量的判别准则
- 只有用字面量初始化的const常量才会进入符号表
字面量:字面量是指由字母,数字等构成的字符串或者数值,它只能作为右值出现,所谓右值是指等号右边的值,如:int a=123这里的a为左值,123为右值。
- 使用其它变量初始化的const常量仍然是只读变量(变量被修饰const后只读属性,被其它变量初始化不影响只读属性)
int a = 1;
const int b = a;
- 被volatile修饰的const常量不会进入符号表(变量被同时修饰时只是只读变量,不能进入符号表,const只是说明变量具有只读属性)
在编译期间不能直接确定初始值的const标识符,都被作为只读变量处理。
2)const引用的类型与初始化变量的类型
- 相同:初始化变量成为只读变量
- 不同:生成一个新的只读变量
编程实验
const经典问题分析
12-1.cpp
#include <stdio.h>
int main()
{
const int x = 1; //真正意义的常量,进入符号表,有内存,只是没使用
const int& rx = x; //只读变量,对应的内存空间为x的,引用的本质是指针
int& nrx = const_cast<int&>(rx); //这里并没有去掉rx只读属性
nrx = 5;
printf("x = %d\n", x);
printf("rx = %d\n", rx);
printf("nrx = %d\n", nrx);
printf("&x = %p\n", &x); //&x = 0xbfc0911c
printf("&rx = %p\n", &rx); //&rx = 0xbfc0911c
printf("&nrx = %p\n", &nrx); //&nrx = 0xbfc0911c
return 0;
}
操作:
1) 编译:g++ 12-1.cpp -o 12-1.out,打印结果:
x = 1
rx = 5
nrx = 5
&x = 0xbf8946e4
&rx = 0xbf8946e4
&nrx = 0xbf8946e4
测试:类型转换后,修改变量数值:
#include <stdio.h>
int main()
{
const int x = 1; //真正意义的常量,进入符号表,有内存,只是没使用
const int& rx = x; //只读变量,对应的内存空间为x的,引用的本质是指针
int& nrx = const_cast<int&>(rx);//这里并没有去掉rx变量本身只读属性
rx = 5; //error。不能给只读变量rx赋值
nrx = 5;
printf("x = %d\n", x);
printf("rx = %d\n", rx);
printf("nrx = %d\n", nrx);
printf("&x = %p\n", &x); //&x = 0xbfc0911c
printf("&rx = %p\n", &rx); //&rx = 0xbfc0911c
printf("&nrx = %p\n", &nrx); //&nrx = 0xbfc0911c
return 0;
}
编译报错:
12-1.cpp:10:5: error: assignment of read-only reference 'rx'
rx = 5;
错误:给只读参数'rx'赋值
分析:
int& nrx = const_cast<int&>(rx);并没有去掉rx变量只读属性,更像拷贝一份数据,将拷贝的数据进行类型转换后给int& nrx。
2) 修改代码:
#include <stdio.h>
int main()
{
volatile const int y = 2; //只读变量,没有进入符号表
int* p = const_cast<int*>(&y); //(&y)取y内存地址,然后转换成指针类型
// y = 7; //error。不能给只读变量y赋值
*p = 6;
printf("y = %d\n", y);
printf("p = %p\n", p);
const int z = y;
p = const_cast<int*>(&z); //去掉const属性,(&z)取z内存地址
*p = 7;
printf("z = %d\n", z);
printf("p = %p\n", p);
return 0;
}
编译:g++ 12-1.cpp -o 12-1.out,打印结果:
y = 6
p = 0xbf95e374
z = 7
p = 0xbf95e378
栈中连续定义的两个int*变量地址差4。
3) 修改代码:
#include <stdio.h>
int main()
{
char c = 'c';
char& rc = c; //引用c
const int& trc = c; //等价==>const int* const trc这个变量一旦定义什么都不能修改。如果去掉const会报错:用表达式'char'类型,非法初始化'int&'类型参数。这里编译器进行了优化。
rc = 'a';
printf("c = %c\n", c); //a
printf("rc = %c\n", rc); //a
printf("trc = %c\n", trc); //c
return 0;
}
编译:g++ 12-1.cpp -o 12-1.out,打印结果:
c = a
rc = a
trc = c
二、关于引用的疑问
引用与指针有什么关系?如何理解“引用的本质就是指针常量”?
1)指针是一个变量
- 值为一个内存地址,不需要初始化,可以保存不同的地址
- 通过指针可以访问对应内存地址中的值
- 指针可以被const修饰成为常量或者只读变量
2)引用只是一个变量的新名字
- 对引用的操作(赋值,取地址等)都会传递到代表的变量上
- const引用使其它代表的变量具有只读属性
- 引用必须在定义时初始化,之后无法代表其它变量
3)从使用C++语言的角度来看
- 引用与指针没有任何的关系
- 引用是变量的新名字,操作引用就是操作对应的变量
4)从C++编译器的角度来看
- 为了支持新概念“引用”必须要有一个有效的解决方案
- 在编译器内部,使用指针常量(T* const p)来实现“引用”
- 因此“引用”在定义时必须初始化(p为固定值)
5)在工程项目开发中
- 在进行C++编程时,直接站在使用的角度看待引用,与指针毫无关系,引用就是变量的别名
- 当对C++代码进行调试分析时,一些特殊情况,可以考虑站在C++编译器角度看待引用
下面的代码有问题吗?
int a = 1;
int b = 2;
int* pc = new int(3);
int& array[] = {a, b, *pc};//error
编程实验
引用典型问题分析
12-2.cpp
#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
//C++不支持引用数组:数组内存是递增的,但是这样引用破坏了数组结构
//error:declaration of ‘array’ as array of references
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;
}
操作:
1) g++ 12-2.cpp -o 12-2.out编译错误,打印结果:
12-2.cpp:17:13: error: declaration of 'array' as array of references
int& array[] = {a, b, *pc};
错误:声明array作为数据的引用。
2) 修改代码,注释掉引用数据:
#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
//C++不支持引用数组:数组内存是递增的,但是这样引用破坏了数组结构
//error:declaration of ‘array’ as array of references
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;
}
g++ 12-2.cpp -o 12-2.out编译正常,打印结果:
&sv.x = 0x804a028
&sv.y = 0xbf94eb8c
&sv.z = 0x91e8008
小结:
1)指针是一个变量
2)引用是一个变量的新名字
3)const引用能够生成新的只读变量
4)在编译器内部使用指针常量实现“引用”(不能引用数组)
5)编译时不能直接确定初始值的const标识符都是只读变量