C++ const 完整语法整理
C++ const 完整语法整理
const 是 C++ 核心的类型限定符,核心作用是给数据 / 函数添加 “只读” 约束,编译阶段强制检查修改行为,既提升代码安全性、可读性,也帮助编译器做优化。以下按「使用场景」拆解const的语法、作用和核心规则,覆盖从基础到类成员的全场景。
一、核心定义
const(常量)本质是 “只读契约”:
- 编译期检查:禁止修改被
const修饰的内容,违规直接报编译错误; - 无运行期开销:仅在编译阶段生效,不额外占用内存(除非对
const变量取地址 / 声明为全局 / 静态); - 核心价值:避免意外篡改数据、明确函数 “只读行为”、支持 const 对象调用。
二、分场景语法详解
场景 1:const 修饰普通变量(全局 / 局部)
语法(两种等价写法)
// 写法1:const 在前(推荐,语义更清晰) const 类型 变量名 = 初始值; // 写法2:const 在后(效果一致,顺序不影响) 类型 const 变量名 = 初始值; 核心作用
变量一旦初始化,终身不可修改;且const变量必须初始化(否则编译报错)。
示例
// 局部const变量(栈上) const double PI = 3.14159; // PI = 3.14; // 错误:只读变量不可赋值 int const num = 10; // 等价于const int num // 全局const变量(全局区,默认作用域为当前文件) const int GLOBAL_NUM = 100; // 跨文件访问全局const:加extern extern const int GLOBAL_NUM2 = 200; 注意事项
- 局部
const变量:若用字面量初始化(如3.14),编译器可能 “常量折叠”(直接替换值,不访问内存); - 全局
const变量:默认带static属性(仅当前文件可见),跨文件访问需加extern。
场景 2:const 修饰指针(重点!三种核心场景)
核心口诀:左数右指
const在*左边 → 修饰「指针指向的内容」(数据只读,指针可移动);const在*右边 → 修饰「指针本身」(指针地址只读,指向的内容可改);
| 场景 | 语法 | 核心约束 | 示例 |
|---|---|---|---|
| 数据只读,指针可移 | const 类型* 指针名 | 不能改*指针名,可改指针名 | const int* p = &a; // *p=20错,p=&b对 |
| 指针只读,数据可改 | 类型* const 指针名 | 必须初始化,可改*指针名,不能改指针名 | int* const p = &a; // *p=20对,p=&b错 |
| 数据 + 指针都只读 | const 类型* const 指针名 | 都不可改,必须初始化 | const int* const p = &a; // 全只读 |
示例
int a = 10, b = 20; // 场景1:数据只读,指针可移 const int* p1 = &a; // *p1 = 30; // 错误:内容只读 p1 = &b; // 正确:指针可移动 // 场景2:指针只读,数据可改 int* const p2 = &a; *p2 = 30; // 正确:内容可改 // p2 = &b; // 错误:指针地址只读 // 场景3:全只读 const int* const p3 = &a; // *p3 = 30; // 错误 // p3 = &b; // 错误 场景 3:const 修饰引用(const 引用)
语法
const 类型& 引用名 = 变量/常量/临时值; 核心作用
- 禁止通过引用修改原变量(引用只读);
- 可绑定常量 / 临时值(普通引用只能绑定可修改的左值);
- 绑定临时值时,临时值的生命周期会被延长至引用的生命周期结束。
示例
int a = 10; const int& ref1 = a; // ref1 = 20; // 错误:引用只读 const int& ref2 = 100; // 正确:const引用可绑定常量 double b = 3.14; const int& ref3 = b; // 正确:临时值(int(b))绑定到const引用,生命周期延长 典型用途
函数参数传递(避免拷贝 + 防止修改):
// 自定义类型参数:const& 避免拷贝,且禁止修改参数 void printCircle(const Circle& c) { cout << c.calculateArea() << endl; // 只能调用c的const成员函数 } 场景 4:const 修饰函数参数
语法
// 基础类型(传值,const仅约束函数内) void func(const int num) { /* num不可改 */ } // 自定义类型(推荐const&,避免拷贝+防修改) void func(const Circle& c) { /* c不可改 */ } // 指针参数(const修饰指向的内容) void func(const int* p) { /* *p不可改 */ } 核心作用
- 禁止函数内修改参数值,提升代码安全性;
- 对自定义类型(如
Circle),结合引用(const&)可避免拷贝,大幅提升效率; - 基础类型(
int/double)传值时加const,仅约束函数内,对外部无影响(可加可不加)。
场景 5:const 修饰函数返回值
语法与作用
仅对「指针 / 引用返回值」有实际意义(基础类型返回值是拷贝,const 无作用):
// 场景1:返回const指针(禁止修改返回的内容) const int* func1(int* arr) { return arr; } // 场景2:返回const引用(禁止外部修改类成员) class Circle { private: double radius; public: const double& getRadius() const { return radius; } }; 示例
int arr[] = {1,2,3}; const int* p = func1(arr); // *p = 10; // 错误:返回的指针指向内容只读 Circle c(5.0); // c.getRadius() = 10; // 错误:返回的引用只读 场景 6:const 修饰类成员(核心!分变量 + 函数)
子场景 6.1:const 成员变量(类内常量)
语法(两种初始化方式)
- C++11 及以上:类内直接初始化(简洁);
- 所有 C++ 版本:构造函数初始化列表初始化(兼容推荐)。
// 写法1:C++11+ 类内直接初始化 class Circle { private: const double PI = 3.14159; // 类内初始化const成员 double radius; public: Circle(double r) : radius(r) {} }; // 写法2:初始化列表初始化(兼容所有版本) class Circle { private: const double PI; // 仅声明,不赋值 double radius; public: // 必须在初始化列表初始化const成员(不能在构造函数体赋值) Circle(double r) : PI(3.14159), radius(r) {} }; 关键补充:静态 const 成员变量
若常量是所有对象共享的(如 PI),加static修饰,减少内存开销(所有对象共用一份):
class Circle { private: static const double PI = 3.14159; // 静态常量,类内初始化 double radius; }; // (可选)类外定义(若需跨文件访问) const double Circle::PI = 3.14159; 子场景 6.2:const 成员函数(常量成员函数)
语法
返回值 函数名(参数列表) const; // const在参数列表后 核心作用
- 约束函数不修改对象状态:
this指针变为const 类名*(只读指针),不能修改类的非mutable成员变量; - 支持 const 对象调用:const 对象只能调用 const 成员函数(非 const 对象可调用 const / 非 const 函数);
- 语义标注:明确函数是 “只读操作”,提升可读性。
示例(Circle 类)
class Circle { private: double radius; mutable int calcCount = 0; // mutable:const函数中可修改 public: double calculateArea() const { // const成员函数 calcCount++; // 合法:mutable变量不受const约束 // radius = 10; // 错误:不能修改非mutable成员 return PI * radius * radius; } void setRadius(double r) { // 非const成员函数 radius = r; } }; // 使用场景 const Circle c1(5.0); c1.calculateArea(); // 正确:const对象调用const函数 // c1.setRadius(6.0); // 错误:const对象不能调用非const函数 Circle c2(5.0); c2.calculateArea(); // 正确:非const对象可调用const函数 c2.setRadius(6.0); // 正确:非const对象调用非const函数 补充:mutable 关键字(const 函数的例外)
mutable修饰的成员变量,可突破 const 函数的约束(用于统计、缓存等不影响对象核心状态的场景):
class Circle { private: mutable int calcCount = 0; // 可在const函数中修改 public: double calculateArea() const { calcCount++; // 合法:统计函数调用次数 return PI * radius * radius; } }; 场景 7:const 修饰对象(const 对象)
语法
const 类名 对象名(构造参数); 核心作用
对象只读:只能调用 const 成员函数,不能修改任何成员变量(包括非 const 成员)。
示例
const Circle c(5.0); // c.setRadius(6.0); // 错误:const对象不能调用非const函数 cout << c.calculateArea() << endl; // 正确:调用const函数 三、核心规则与易错点
- const 是编译期约束:仅在编译阶段检查,若通过
const_cast强制修改 const 变量,会导致 “未定义行为”(可能崩溃 / 数据异常): - const 的顺序:
- 普通变量:
const int a和int const a等价; - 指针:
const int* p和int const* p等价(左数),int* const p(右指)不同;
- 普通变量:
- const 引用绑定临时值:临时值(如
10、表达式结果)的生命周期会被 const 引用延长至引用生命周期结束; - const 成员函数调用规则:
- const 成员函数 → 只能调用其他 const 成员函数(避免间接修改对象);
- 非 const 成员函数 → 可调用 const / 非 const 成员函数。
const int num = 10; int* p = const_cast<int*>(&num); *p = 20; // 未定义行为!编译器可能优化num为常量折叠,num仍为10 四、常量正确性(Const Correctness)规范
遵循以下规范,可大幅减少编程错误:
- 所有只读数据(如 PI)用
const修饰; - 所有不修改对象状态的成员函数加
const; - 函数参数若无需修改,加
const(尤其是自定义类型的引用 / 指针参数); - const 对象仅调用 const 成员函数,非 const 对象按需调用;
- 静态常量成员加
static const,减少内存开销。