C++中的经典问题分析
(一)const
问题:const修饰的变量(标识符)什么时候是只读变量,什么时候是真正意义上的常量?
(1)const + 标识符
C++编译器对const修饰的标识符的判别准则:
-
用
数值常量(字面量)
直接初始化const 变量
,则数值常量(字面量)
和const 变量都直接放入符号表.
eg:const int a = 1;
表示a此时为一个真正意义上的常量。 -
用
其他变量
直接初始化const 变量
,则const 变量是只读变量。
eg:int a = 1; const int b = a;
表示b是只读变量。 -
被
volatile
修饰的const 变量
,const 变量不会进入符号表,const变量是只读变量。
volatile const int a = 1; //正确:此时a是一个只读变量
注意:volatile
和const
修饰的标识符,不能当作左值,即放在 = 左边。eg:
volatile const int a;
a = 1;//错误:变量a是只读变量,不能为左值
总结:在编译期间,不能直接确认初始值的const变量,都会被当作只读变量处理。
(2)const + 引用
const 引用是让变量拥有只读属性:const type& name = var;
1)变量:const int& b = a;
int a = 1;
const int& b = a;
int * p = (int*)&b;
b = 5; 错误:a/b为只读变量,不可赋值
*p = 8; 正确:兼容C语法可以使用指针修改变量a/b的值。
2)数值常量(字面量):const int& b = 1;
const int& b = 1; 编译器为常量1分配4个字节的空间,b为其别名
int* p = (int*)&b; p指向b这段空间的指针
b = 3; 错误:b为只读变量,不可赋值更改
*p = 5; 正确:为了兼容C语法,可修改引用变量b的值
注意:使用数值常量(字面量)对引用进行初始化赋值,c++编译器会为常量分配空间,并且把引用名作为这一段空间的引用别名。此时引用变量是一个只读变量。
1 #include <stdio.h>
2
3 int main(int argc, const char *argv[])
4 {
5 const int a = 1;
6 const int& b = a;
7 int &c = const_cast <int&>(b);
8 c = 6;
9 printf("a = %d, b = %d, c = %d\n", a , b, c);
10 printf("a = %p, b = %p, c = %p\n", &a , &b, &c);
11
12 return 0;
13 }
~
执行结果如下:
linux@ubuntu:~/test$ g++ demo.cpp
linux@ubuntu:~/test$ ./a.out
a = 1, b = 6, c = 6
a = 0xbfe74a54, b = 0xbfe74a54, c = 0xbfe74a54
-
const int a = 1;
1)表示定义了字面数值为1的常量a。此时a是一个真正意义上的常量。
2)同时C++编译器会将const 常量a和字面值1放入符号表中,并为const常量a开辟一段内存空间。
平时这段内存空间是不怎么使用的,在对常量a定义指针或者定义引用时,就可以使用这段空间了。
3)指针/引用修改值时,其实修改的是这段内存空间,而不是修改常量a的值。因为常量a的值是放在符号表中的,用到变量a时,其值是从符号表中取值的。所以无论怎么改变内存空间中的值,使用常量a时,其值始终为初值1. -
const int a = 1; const int& b = a;
代表此时const 引用b表示一个只读变量。b指向的内存空间是C++编译器为常量a开辟那一段内存空间,起别名为b。 -
int &c = const_cast <int&>(b);
const_cast
消除源目标的只读属性,此时消除只读变量b的只读属性,变量c变成为一个普通变量,指向的是b对应的那一段内存空间。 -
volatile const int a = 1;
1)不加volatile时,编译器会将经常访问的变量和其值都从内存中拷贝一份放入寄存器中,每次取变量的值,都是从寄存器中取走。但是当内存中变量的值修改时,寄存器来不及修改变量值,因此每次访问使用变量a还是从寄存器中取值。这样就会事与愿违。
2)加了volatile后,会让编译器不对变量a进行优化,不把变量a放入寄存器中,每次取值必须重新从内存中重新读取,此时变量a具有实时性。
3)volatile const修饰的变量a,此时是一个只读变量。
C++为了兼容C语言的const使用,仍然为const变量分配使用空间。只不过一般情况下这个空间不使用,只有通过指针或引用时才使用。
案例代码(1):
volatile const int a = 1;
const int& b = const_cast<&int>(a);
int * p = const_cast<int*>(&b);
结果:
linux@ubuntu:~/test$ g++ demo.cpp -Wall
linux@ubuntu:~/test$ ./a.out
a = 1, b = 6, *p = 6
&a = 0xbf8e08e0, &b = 0xbf8e08e4, p = 0xbf8e08e4
由此可见:b的类型为const int&
引用类型;a的类型为const volatile int
常整型,类型不同不能用
a和b的类型不同,根据初始化的类型和引用类型不同,因此b生成为新的只读变量。因此a和b的地址和值不同。
案例代码(2):
13 char ch = 'c';
14 const char& c = ch;
15 int* p = const_cast<int*>(&c);
结果:
error: invalid const_cast from type ‘const char*’ to type ‘int*’
const_cast:不能用于不同数据类型间的强转:char->int
应该为:
char ch = 'c';
const char& c = ch;
char* p = const_cast<char*>(&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;
}
C++为了兼容C语言中的:数组是在内存空间中连续顺序排列,C++语法中放弃了引用数组的使用。
因此C++中不支持引用数组,可使用结构体类型替代。