C++ const 修饰符深入浅出详解
📅 更新时间:2025年6月6日
🏷️ 标签:C++ | const关键字 | 常量 | 多文件编程 | 现代C++
文章目录
- 前言
- 🌟 一、const 是什么?为什么要用?
- 示例
- ✅ const 的四大好处
- 🧨二、const 在变量声明中的位置
- const变量必须初始化
- 📚三、编译器如何处理 const 修饰的变量
- 注意
- 技巧
- 🚀四、const的引用
- 介绍
- 思考1
- 思考2
- 🎯五、指针和const
- 指向常量的指针(pointer to const)
- 指针常量vs常量指针
- 指针常量
- 常量指针
- 🚀 六、const 与 constexpr:常量表达式的进阶
- 🛠 示例代码
- 🧩总结
前言
✨ 本文将以通俗易懂的方式,循序渐进地讲解 C++ 中 const 修饰符的多种用法,涵盖普通变量、指针、函数参数、成员函数、返回值以及与 constexpr 的结合。通过清晰的示例代码、常见误区分析和最佳实践建议,带你从基础到进阶,彻底掌握 const 的核心原理与应用场景,助你编写更安全、更高效的 C++ 代码
提示:以下是本篇文章正文内容,下面案例可供参考
🌟 一、const 是什么?为什么要用?
const 是 C++ 中的核心关键字,用于声明不可修改的变量或函数契约。它的核心作用是限制修改,从而提升代码的安全性、可读性和可维护性
示例
const 修饰普通变量是最简单的用法,表示变量值不可修改,且必须初始化
const int x = 10;
x = 20; // ❌ 编译错误,x 是只读的
#include <iostream>
const int maxSize = 100;
int main() {
// maxSize = 200; // ❌ 错误,不能修改
std::cout << "Max Size: " << maxSize << std::endl;
return 0;
}
✅ const 的四大好处
防止误修改:避免无意中改变变量值,减少 bug。
增强函数语义:清晰表达函数或参数的只读意图。
编译期检查:让编译器帮你捕捉非法修改行为。
优化性能:编译器可对 const 变量进行优化(如存储在只读内存)
🧨二、const 在变量声明中的位置
const
关键字通常放在变量类型之前,例如:
const int a=10;
也可以放在类型之后,但这种用法较少见:
int const a=10;
可以用一个变量初始化常量, 也可以将一个常量赋值给一个变量
//可以用一个变量初始化常量
int i1 = 10;
const int i2 = i1;// 可以使用一个非 const 的变量(例如 i1)来初始化一个 const 变量(例如 i2)
//也可以将一个常量赋值给一个变量
int i3 = i2;//i2是一个const的变量 可以赋给一个 非const的变量
const变量必须初始化
//错误用法,const变量必须初始化
//const int i4;
这个时候我们会有一个想法,在头文件中如果使用了cosnt,那说明必须要对变量初始化,但头文件中不是只能声明变量吗????两者会不会引发什么问题
我们接着往下看
📚三、编译器如何处理 const 修饰的变量
const
修饰的变量在编译时会被视为只读,尝试修改其值会导致编译错误。此外,编译器可能会对 const 变量进行优化,如将其存储在只读内存区域
注意
默认状态下,const对象仅在文件内有效
当以编译时初始化的方式定义一个const对象时,就如对bufSize的定义一样:
const int bufSize = 512;
编译器将在编译过程中把用到该变量的地方都替换成对应的值。也就是说,编译器会找到代码中所有用到bufSize的地方,然后用512替换。
为了执行上述替换,编译器必须知道变量的初始值。
如果程序包含多个文件,则每个用了const对象的文件都必须得能访问到它的初始值才行。要做到这一点,就必须在每一个用到变量的文件中都有对它的定义.
为了支持这一用法,同时避免对同一变量的重复定义,默认情况下,const对象被设定为仅在文件内有效。当多个文件中出现了同名的const变量时,其实等同于在不同文件中分别定义了独立的变量。
我们创建一个global.h文件和global.cpp文件, 我们知道头文件只做变量的声明,之前我们在头文件添加变量的定义会导致连接错误。
那如果我们添加const变量的定义
//global.h
const int bufSize = 100;
在main.cpp和global.cpp中包含global.h,发现可以编译通过,
这是因为虽然main.cpp和global.cpp中包含了同名的bufSize,但却是不同的变量,运行程序可以编译通过
我们来跑一下代码来测试一下是否为不同变量
//global.h
#pragma once
#include<iostream>
const int a=10;
void play();
//global.cpp
#include<iostream>
#include"global.h"
using namespace std;
void play()
{
cout <<"global.cpp中 a 地址:" << &a << endl;
}
//main.cpp
#include<iostream>
#include"global.h"
using namespace std;
int main()
{
cout <<"main.cpp中 a的地址:" << &a << endl;
play();
return 0;
}
输出:
main.cpp中 a的地址:00007FF629C4AC54
global.cpp中 a 地址:00007FF629C4ABB0
我们会发现这是两个不同的变量,所以链接不会爆错
那我们如果就想要同一个变量,咋办????
前置知识点:extern
可以看我前面讲解c++中的extern关键字的文章
如果我们想让所有文件共享一个变量,那我们需要这样写
技巧
如果想在多个文件之间共享const对象,必须在变量的定义之前添加extern关键字
//global.h
#pragma once
#include<iostream>
extern const int a;//只声明
void play();
//global.cpp
#include<iostream>
#include"global.h"
using namespace std;
const int a = 10;
void play()
{
cout <<"global.cpp中 a 地址:" << &a << endl;
}
//main.cpp
#include<iostream>
#include"global.h"
using namespace std;
int main()
{
cout <<"main.cpp中 a的地址:" << &a << endl;
play();
return 0;
}
输出:
main.cpp中 a的地址:00007FF6644EABB0
global.cpp中 a 地址:00007FF6644EABB0
很容易发现,这是同一个变量
🚀四、const的引用
介绍
可以把引用绑定到const对象上,就像绑定到其他对象上一样,我们称之为对常量的引用(reference to const)。与普通引用不同的是,对常量的引用不能被用作修改它所绑定的对象:
//定义常量
const int ci = 1024;
//用常量引用绑定常量
const int &r1 = ci;
不能修改常量引用的值
//不能修改常量引用的值
//r1 = 2048;
也不能用非常量引用指向一个常量对象
//也不能用非常量引用指向一个常量对象
//int& r2 = ci;
术语
常量引用是对const的引用
企业中,C++程序员们经常把词组“对const的引用”简称为“常量引用
允许将const引用绑定一个非const变量
int i5 = 1024;
//允许将const int& 绑定到一个普通的int对象上
const int &r5 = i5;
常量引用绑定字面量
//常量引用绑定字面量
const int &r6 = 1024;
常量引用绑定表达式计算的值
//常量引用绑定表达式计算的值
const int &r7 = r6 * 2;
const int &r8 = i5 * 2 + 10;
思考1
下面的代码能编译通过吗?
double dval = 3.14;
int & rd = dval;
答案
//错误用法,类型不匹配
double dval = 3.14;
int & rd = dval;
思考2
下面的代码能编译通过吗?
double dval = 3.14;
const int & ri = dval;
答案
//编译通过
double dval = 3.14;
const int & ri = dval;
上面的代码相当于
//上面代码会做隐士转换,相当于下面代码
const int temp = dval;
const int &ri = temp;
在这种情况下,ri绑定了一个临时量(temporary)对象。
所谓临时量对象就是当编译器需要一个空间来暂存表达式的求值结果时临时创建的一个未命名的对象
C++程序员们常常把临时量对象简称为临时量。
对const的引用可能引用一个并非const的对象必须认识到,常量引用仅对引用可参与的操作做出了限定,对于引用的对象本身是不是一个常量未作限定。因为对象也可能是个非常量,所以允许通过其他途径改变它的值:
int i9 = 1024;
//非常量引用绑定i9
int &r9 = i9;
//常量引用绑定一个变量
const int &r10 = i9;
//可以同过非常量引用修改i9的值
r9 = 2048;
🎯五、指针和const
指向常量的指针(pointer to const)
可以令指针指向常量或非常量。类似于常量引用,指向常量的指针(pointer to const)不能用于改变其所指对象的值。
要想存放常量对象的地址,只能使用指向常量的指针
:
/PI 是一个常量,它的值不能改变
const double PI = 3.14;
double * ptr = &PI;//错误!!!,ptr是一个普通指针
const double *cptr = &PI;正确,cptr可以指向一个双精度常量
*cptr = 3.14;//错误,不能给*ptr赋值
指针常量vs常量指针
指针常量
这是一个 指针常量(constant pointer)
int * const curErr = &errNumb;
含义:curErr 是一个指向 int 的常量指针
指针本身是常量:不能改变指针指向的对象(即不能让 curErr 指向别的变量)
但可以通过指针修改所指向对象的值(前提是原始对象不是常量)
*curErr = 20; // ✅ 允许修改 errNumb 的值
int another = 42;
curErr = &another; // ❌ 错误!curErr 是 const 指针,不能指向其他地址
常量指针
const int * curErr = &errNumb;
含义:curErr 是一个指向 const int 的指针
所指向的内容是常量:不能通过这个指针修改它指向的对象的值
但是可以改变指针指向的对象(可以指向其他地址)
*curErr = 20; // ❌ 不允许修改,因为 curErr 指向的是 const int
int another = 42;
curErr = &another; // ✅ 允许,指针可以指向另一个地址
🚀 六、const 与 constexpr:常量表达式的进阶
① 常量表达式
值在编译期确定,且不可修改。
示例:const int max = 20;。
② constexpr
C++11 引入,强制编译期计算
,确保常量表达式。
语法:constexpr T var = expr;
constexpr int max = 20;
constexpr int limit = max + 10; // ✅ 编译期计算
// constexpr int sz = getSize(); // ❌ 运行时函数,非 constexpr
constexpr int getConstSize() { return 30; }
constexpr int sz = getConstSize(); // ✅ OK
🛠 示例代码
#include <iostream>
constexpr int max = 20;
constexpr int limit = max + 10;
constexpr int getConstSize() { return 30; }
constexpr int sz = getConstSize();
int main() {
std::cout << "Max: " << max << std::endl; // 输出 20
std::cout << "Limit: " << limit << std::endl; // 输出 30
std::cout << "Size: " << sz << std::endl; // 输出 30
return 0;
}
💡 实践建议
优先使用 constexpr:确保编译期常量,性能更优。
constexpr 函数:保持简单,确保编译期可计算。
全局变量:用 extern constexpr 共享常量
🧩总结
const 是 C++ 编程中不可或缺的工具,通过限制修改提升代码安全性、可读性和性能。从普通变量到指针、函数参数、成员函数、返回值,const 的应用无处不在。掌握 const 的正确用法,能显著提高你的 C++ 编程水平
如果你觉得本文对你有帮助,不妨点赞 + 收藏 + 关注,更多 C++ 系列教程将持续更新 🔥!