1.C语言中的const
1.1 const只读变量
- const修饰的变量是只读的,本质还是变量
- const修饰的局部变量在栈上分配空间
- const修饰的全局变量在全局数据区分配空间
- const只在编译期有用,在运行期无用
- const修饰的变量不是真的变量,它只是告诉编译器该变量不能出现在赋值符号的左边
1.2 const全局变量的分歧
- 在现代C语言编译器中,修改const全局变量将导致程序崩溃
- 标准C语言编译器不会将const修饰的全局变量存储于只读存储区中,而是存储于可修改的全局数据区,其值依然可以改变。
#include <stdio.h>
const int g_cc = 2;
int main()
{
const int cc = 1;
int *p = (int *)&cc;
printf("cc = %d\n", cc);
*p = 3;
printf("cc = %d\n", cc);
p = (int *)&g_cc;
printf("g_cc = %d\n", g_cc);
*p = 4;
printf("g_cc = %d\n", g_cc);
return 0;
}
- gcc、VS2017、BCC(标准C)编译结果:
1.3 const的本质
- C语言中的const使得变量具有只读属性
- 现代C编译器中的const将具有全局生命周期变量存储于只读存储区(所以gcc、vs2017都会出错)
- const不能定义真正意义上的常量
#include <stdio.h>
const int g_array[5] = {0};
void modify(int* p, int v)
{
*p = v;
}
int main()
{
int const i = 0;
const static int j = 0;
int const array[5] = {0};
modify((int *)&i, 1); // ok
modify((int *)&j, 2); // error
modify((int *)&array[0], 3); // ok
modify((int *)&g_array[0], 4); // error
printf("i = %d\n", i);
printf("j = %d\n", j);
printf("array[0] = %d\n", array[0]);
printf("g_array[0] = %d\n", g_array[0]);
return 0;
}
- gcc、BCC(标准C)编译结果:
1.6 const修饰函数参数和返回值
- const修饰函数参数表示在函数体内不希望改变参数的值
- const修饰函数返回值表示返回值不可改变,多用于返回指针的情形
- 小贴士:C语言中的字符串字面量存储于只读存储区,在程序中需要使用 const char* 指针。
#include <stdio.h>
const char* f(const int i)
{
// i = 5; //error: assignment of read-only parameter ‘i’
return "Test";
}
int main()
{
const char* pc = f(0);
printf("%s\n", pc);
// pc[6] = '_'; //test.c:16:11: error: assignment of read-only location ‘*(pc + 6u)’
printf("%s\n", pc);
return 0;
}
- 运行结果
1.7 小结
- const使得变量具有只读属性
- const不能定义真正意义上的常量
- const将具有全局生命期的变量存储于只读存储区
2 C++中的const
2.1 编程实验 test.cpp
#include <stdio.h>
int main()
{
const int c = 0;
int *p = (int *)&c;
printf("Begin...\n");
*p = 5;
printf("c = %d\n", c);
printf("*p = %d\n", *p);
printf("End...\n");
return 0;
}
- vs2017运行结果:
2.2 C++中的const
- C++在C的基础上对const进行了进化处
- 当碰见const声明时在符号表中放入常量
- 编译过程中若发现使用常量则直接以符号表中的值替换
- 编译过程中若发现下述情况则给对应的常量分配空间
- 1. 对const常量使用了extern
- 2. 对const常量使用&操作符
- 注意:C++编译器虽然可能为const常量分配空间,但不会使用其存储空间的值。
- 当遇到&c时,为其分配空间,分配空间只是为了兼容C语言
- C语言中的const变量:C语言中const变量是只读变量,会分配存储空间
- C++中的const常量:可能分配存储空间:
- 当const常量为全局,并且需要在其它文件中使用
- 当使用&操作符对const常量取地址
- C++中的const常量类似于宏定义:const int c = 5;≈ #define c 5
- C++中的const常量与宏定义不同
- const常量由编译器处理,编译器对const常量进行类型检查和作用域检查
- 宏定义由预处理器处理,单纯的文本替换
#include <stdio.h>
void f()
{
#define a 3
const int b = 4; // b的作用域仅在f函数内
}
void g()
{
printf("a = %d\n", a);
//printf("b = %d\n", b); // error: 'b' was not declared in this scope
}
int main(int argc, char *argv[])
{
f();
g();
printf("Press enter to continue ...");
getchar();
return 0;
}
2.3 const常量的判别准则
- 只有用字面量初始化的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);
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);
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;
}
- 运行结果
2.4 小结
- 与C语言不同,C++中的const不是只读变量
- C++中的const是一个真正意义上的常量
- C++编译器可能会为const常量分配空间
- C++完全兼容C语言中const常量的语法特性
- 指针是—个变量
- 引用是一个变量的新名字
- const引用能够生成新的只读变量
- 在编译器内部使用指针常量实现“引用”
- 编译时不能直接确定初始值的const标识符都是只读变量
3. mutable关键字
- mutable是为了突破const函数的限制而设计的
- mutable成员变量将永远处于可改变的状态
- mutable在实际的项目开发中被严禁滥用
- mutable的深入分析
- mutable成员变量破坏了只读对象的内部状态
- const成员函数保证只读对象的状态不变性
- mutable成员变量的出现无法保证状态不变性
- 实例分析
- 统计对象中某个成员变量的访问次数
#include <iostream>
using namespace std;
class Test
{
int m_value;
mutable int m_count;
public:
Test(int value = 0)
{
m_value = value;
m_count = 0;
}
int getValue() const
{
m_count++; //const成员函数,内部不能直接改变成员变量值
return m_value;
}
void setValue(int value)
{
m_count++;
m_value = value;
}
int getCount() const
{
return m_count;
}
};
int main(int argc, char *argv[])
{
Test t;
t.setValue(100);
cout << "t.m_value = " << t.getValue() << endl;
cout << "t.m_count = " << t.getCount() << endl;
const Test ct(200);
cout << "ct.m_value = " << ct.getValue() << endl;
cout << "ct.m_count = " << ct.getCount() << endl;
return 0;
}
- 运行结果
- 更好的解决方案
#include <iostream>
#include <string>
using namespace std;
class Test
{
int m_value;
int * const m_pCount;
/* mutable int m_count; */
public:
Test(int value = 0) : m_pCount(new int(0))
{
m_value = value;
/* m_count = 0; */
}
int getValue() const
{
/* m_count++; */ //只读对象内部不能直接改变
*m_pCount = *m_pCount + 1;
return m_value;
}
void setValue(int value)
{
/* m_count++; */
*m_pCount = *m_pCount + 1;
m_value = value;
}
int getCount() const
{
/* return m_count; */
return *m_pCount;
}
~Test()
{
delete m_pCount;
}
};
int main(int argc, char *argv[])
{
Test t;
t.setValue(100);
cout << "t.m_value = " << t.getValue() << endl;
cout << "t.m_count = " << t.getCount() << endl;
const Test ct(200);
cout << "ct.m_value = " << ct.getValue() << endl;
cout << "ct.m_count = " << ct.getCount() << endl;
return 0;
}