版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
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;
-
}