【C++ 基础】const 关键字
0 概览
本文开发环境为 Linux 操作系统(x86) + C++,概览如下:
- const 的含义
- const 能修饰什么?分别有什么作用
- const 修饰变量
- const 修饰指针
- const 修饰引用
- const 修饰函数
- const 修饰类
- const 与 define 的区别
1 const 含义
被它修饰的值不能改变,是只读变量。必须在定义的时候就给它赋初值(如果是类成员变量也可使用初始化列表进行初始化)
2 const 与指针
const 与指针分别 3 种
- 指向常量的指针(pointer to const)
- 自身是常量的指针(常量指针,const pointer)
- 前面两种的组合:指向常量的常指针
2.1 指向常量的指针(pointer to const)/ 底层 const
指向常量的指针(pointer to const):定义了一个指针,这个指针指向一个只读的对象,不能通过该指针来改变这个对象的值。
特点:强调的是指针所指向的对象的不可改变性
底层 const:底层 const(low-level const)表示指针所指得对象是一个常量
形式:
const 数据类型 *指针变量 = 变量名;
数据类型 const *指针变量 = 变量名;
示例代码:
int a = 2;
int b = 3;
// 以下两种写法都可
const int *p = &a;
int const *p = &a;
// 更改
*p = 10; // 错误,不可更改指向对象的值
*p = &b; // 正确,可以指向别的对象
a = 10; // 正确,在原对象上修改
2.2 自身是常量的指针(常量指针,const pointer)/ 顶层 const
自身是常量的指针(常量指针,const pointer):定义了一个指针,这个指针的值只能在定义时初始化。
特点:强调的是指针的不可改变性
顶层 const:顶层 const(top-level const)表示指针本身是一个常量
形式:
const 数据类型 *指针变量 = 变量名;
数据类型 const *指针变量 = 变量名;
示例代码:
int a = 2;
int b = 3;
int *const p = &a;
// 更改
*p = 10; // 正确,可以改变指向对象的值
p = &b; // 错误,不可指向别的对象
a = 10; // 正确,在原对象上修改
2.3 指向常量的常指针
指向常量的常指针:是前面两种指针类型的组合(3.1,3.2)
特点:既不可改变指向的地址 且 指向的对象的内容不可透过指针修改
形式:
const 数据类型 *const 指针变量 = 变量名;
示例代码:
int a = 2;
int b = 3;
const int *const p = &a;
// 更改
*p = 10; // 错误,不可更改指向对象的值
p = &b; // 错误,不可指向别的对象
a = 10; // 正确,在原对象上修改
2.4 记忆方式
第一种方式:利用英文从右边往左边读,并且以 to 为分界,to 之前为描述指针的特性,to 之后为描述指针指向的对象的特性
// (1) p is a pointer to const char
// (1) p 是一个指针指向 const char,即指针所指向的 char 变量的值不可透过指针修改
const char *p;
char const *p;
//(2)p is a const pointer to char
//(2)p 是一个 const 指针指向一个 char,即 p 不可改变指向的地址
char *const p;
//(3)p is a const pointer to const char
//(3)p 是一个 const 指针指向 const char,即 p 不可改变指向的地址,也不可改变指向的对象的值
const char *const p;
第二种方式:
如果 const 位于 * 的左侧,则 const 就是用来修饰指针所指向的对象,即指针指向的对象不可透过指针修改
如果 const 位于 * 的右侧,则 const 就是修饰指针本身,即指针本身是常量,不可改变指向的地址
2.5 小结
1、当指向的目标特性为 const,则指针指向的对象的内容不可透过指针修改
2、当指针被加上 const 特性,则指针不可改变指向的地址
3、当上述两种都包含的情况,则指针不可改变指向的地址 且 指针指向的对象的内容不可透过指针修改
3 const 与引用
只有一种
- 对常量的引用(reference to const)
- 没有 const reference,因为引用只是对象的别名,引用不是对象,不能用 const 修饰
3.1 对常量的引用(reference to const)- 简称常量引用
特点:不能修改他所绑定的对象
形式:
const 数据类型 &引用变量 = 变量名;
示例代码 1 引用及其对应的对象都是常量:
const int a = 2;
const int &reference1 = a; // 正确:引用及其对应的对象都是常量
int &reference2 = &a; // 错误:不能让一个非常量引用指向一个常量对象
reference1 = 10; // 错误:常量引用不不能修改他所绑定的对象
示例代码 2 引用为常量,其对应的对象不是常量:
int a = 2;
const int &reference1 = a; // 正确:引用是常量,其对象不是常量
int &reference2 = &a; // 正确:引用和其对象都不是常量
reference1 = 10; // 错误:常量引用不不能修改他所绑定的对象
reference2 = 10; // 正确:普通引用可以修改他所绑定的对象
4 const 与函数
4.1 const 修饰函数返回值
示例代码:
const int function5(); // 返回一个常数
const int* function6(); // 返回一个指向常量的指针变量,使用:const int *p = function6();
int* const function7(); // 返回一个指向变量的常指针,使用:int* const p = function7();
4.2 const 修饰函数参数
示例代码:
void function1(const int Var); // 传递过来的参数在函数内不可变
void function2(const char* Var); // 参数指针所指内容为常量
void function3(char* const Var); // 参数指针为常量
void function4(const int& Var); // 引用参数在函数内为常量
5 const 与类
总结:
- 常对象成员可以在定义的时候赋值或使用初始化列表进行初始化
- 常对象成员函数可用于对重载函数的区分
- 常对象只能调用常成员函数
示例代码 1(常对象成员可以使用初始化列表进行初始化):
class A
{
private:
const int a; // 常对象成员,只能使用初始化列表进行初始化
public:
// 构造函数
A() : a(0) { }; // 初始化列表
A(int x) : a(x) { }; // 初始化列表
// const 可用于对重载函数的区分
int getValue(); // 普通成员函数
int getValue() const; // 常成员函数,不得修改类中的任何数据成员的值
};
void function()
{
// 对象
A b; // 普通对象,可以调用全部成员函数
const A a; // 常对象,只能调用常成员函数
}
示例代码 2(常对象成员不能被赋值):
#include <stdio.h>
#include <string>
using namespace std;
class MyClass {
public:
const int myConstMember;
MyClass();
void setMember();
};
void MyClass::setMember()
{
myConstMember = 3; // 因为常量不可被修改,因此会报错
}
int main(int argc, char* argv[])
{
MyClass testClass;
return 0;
}
运行截图,如下所示:
示例代码 3(常对象成员可以在定义的时候赋值):
#include <stdio.h>
#include <string>
#include <iostream>
using namespace std;
class MyClass {
public:
const int myConstMember = 4;
};
int main(int argc, char* argv[])
{
MyClass testClass;
cout << "myConstMember:" << testClass.myConstMember << endl;
return 0;
}
运行截图,如下所示:
6 const 与 #define 的区别
宏定义 #define | const 常量 |
---|---|
宏定义,相当于字符替换 | 常量声明 |
预处理器处理 | 编译器处理 |
无类型安全检查 | 有类型安全检查 |
不分配内存 | 要分配内存 |
存储在代码段 | 局部常量存放在栈区;全局常量编译期一般放在符号表中,不分配内存;字面值常量,比如字符串,存放在常量区 |
提问:上述表格有提到数据段和代码段,栈区,常量区,他们究竟是什么?对于内存来说还有其他段吗,他们分别存储程序中的什么呢?
可以参考下一篇博客:【八股文 02】C++ 进程内存布局及其相关知识- https://www.cnblogs.com/PikapBai/p/17577466.html
7 总结
- 修饰变量,说明该变量不可以被改变
- 修饰指针
- 指向常量的指针(pointer to const):强调的是指针所指向的对象的不可改变性
- 自身是常量的指针(常量指针,const pointer):强调的是指针的不可改变性
- 前面两种的组合:指向常量的常指针:既强调指针所指向的对象的不可改变性又强调指针的不可改变性
- 修饰引用
- 指向常量的引用(reference to const):不能修改他所绑定的对象。用于形参类型,即避免了拷贝,又避免了函数对值的修改:
- 修饰类成员函数,可用于重载函数的区分,也说明该成员函数内不能修改成员变量
- 修饰类成员变量,只能在赋值时初始化或使用初始化列表进行初始化
- 修饰类,只能调用调用类常成员函数
8 参考资料
1、《C++ Primer 第五版》- 2.4 const 限定符
2、const那些事 - 作者 Light-City - https://light-city.github.io/basic_content/const/#4
3、interview - 作者 huihut - https://github.com/huihut/interview 或 https://interview.huihut.com/#/?id=const