1const的本质与承诺
const关键字的核心是做出一个“承诺”:承诺其所修饰的对象(变量、指针、引用、成员函数等)的“不变性”。它告诉编译器和代码的阅读者:“这个值/这个对象的状态,在这里不会被改变。”
编译器会强制检查这个承诺,违反则报错。这增强了代码的安全性、可读性和可维护性。
2const的常见用法“八股”
2.1常量变量
- 作用:定义一个编译期或运行期的常量,值初始化后不可修改。
- 与
#define宏的区别:const常量有类型检查,更安全。const常量在编译、链接时遵循作用域规则,而宏是简单的文本替换,无作用域。const常量从汇编角度看,通常会被编译器优化。
constintMAX_SIZE=100;// 编译期常量(通常)constintvalue=getValue();// 运行期常量// MAX_SIZE = 200; // 错误:不可修改2.2const与指针
这是经典的面试考点,关键看const修饰的是谁。
常量指针(指针是常量)
inta=10,b=20;int*constp=&a;// p 本身是常量,指向不能变// p = &b; // 错误:p 的指向不可修改*p=30;// 正确:p 指向的值(a)可以被修改口诀:
const在*右边,指针本身不可变。指向常量的指针(指向的数据是常量)
inta=10,b=20;constint*p=&a;// 等价于 int const* p// *p = 30; // 错误:不能通过 p 修改 ap=&b;// 正确:p 的指向可以改变口诀:
const在*左边,指向的数据不可变(通过该指针)。指向常量的常量指针(两者都不可变)
inta=10;constint*constp=&a;// p = &xxx; // 错误// *p = 20; // 错误
2.3const与引用
- 常量引用:不能通过该引用修改所绑定的对象。这是最常用的
const引用形式,常用于函数参数,避免拷贝,同时保证不修改实参。intx=5;constint&ref=x;// ref 是 x 的常量引用// ref = 10; // 错误x=10;// 正确,但 ref 看到的值也变成了 10 - 关键特性:常量引用可以绑定到右值(临时对象、字面量),这是普通引用做不到的。
constint&r=42;// 合法// int& r = 42; // 非法voidfunc(conststd::string&str);// 可以接收字符串字面量:func(“hello”)
2.4const成员函数
这是类设计的核心,用于表明该成员函数不会修改对象的成员变量(除非成员被mutable修饰)。
- 语法:在成员函数声明的参数列表后加上
const。 - 作用:
- 安全:编译器保证函数内不会修改非
mutable的成员。 - 设计意图:告诉使用者这是一个“只读”操作。
- 关键作用:
const对象只能调用const成员函数。这使得程序能正确地对常量对象进行操作。
- 安全:编译器保证函数内不会修改非
classMyClass{public:intgetValue()const{// const 成员函数// value_ = 10; // 错误!不能修改成员变量returnvalue_;}voidsetValue(intv){// 非 const 成员函数value_=v;}private:intvalue_;mutableintcache_;// mutable 成员即使在 const 函数中也可被修改};constMyClass obj;intx=obj.getValue();// 正确:const 对象调用 const 函数// obj.setValue(5); // 错误:const 对象不能调用非 const 函数- 重载依据:
const可以作为成员函数重载的依据。编译器根据调用对象的const性来选择版本。classText{char&operatorstd::size_t pos{returntext[pos];}// 用于普通对象constchar&operatorstd::size_t posconst{returntext[pos];}// 用于 const 对象};
2.5const与函数参数和返回值
- 参数:使用
const引用(const T&)传递不希望被修改的大型对象,效率高且安全。使用const指针(const T*)同理。 - 返回值:
- 返回
const值(如const T):对于内置类型意义不大,对于类对象可以防止返回值被用作左值(如(a*b) = c;这种无意义的操作),但在 C++11 后的移动语义下较少用。 - 返回
const引用/指针:常见于返回成员变量的引用/指针,并希望调用者不能通过它修改对象内部状态。但要警惕返回指向局部对象的引用/指针!
- 返回
2.6顶层const与底层const
这是 C++ 标准中的精确概念,有助于理解复杂的类型声明。
- 顶层
const:表示对象本身是常量。适用于任意数据类型。inta=0;int*constp1=&a;// p1 本身是 const(顶层)constintb=0;// b 本身是 const(顶层) - 底层
const:表示指针或引用所指向的对象是常量。constint*p2=&a;// p2 指向的对象是 const(底层)constint&r=a;// r 指向的对象是 const(底层)constint*constp3=&a;// 左边底层 const,右边顶层 const - 在拷贝时的区别:
- 顶层
const不影响拷贝,因为拷出和拷入的对象是独立的。 - 进行拷贝时,拷入和拷出的对象必须具有相同的底层
const资格,或者能从非const转换为const(反之则不行)。
constintc=10;intd=c;// 正确:顶层 const 被忽略constint*p4=&a;// 底层 constint*p5=p4;// 错误:p4 有底层 const,p5 没有constint*p6=p4;// 正确:两者都有相同的底层 const - 顶层
3 进阶与相关考点
mutable关键字:用于修饰类的成员变量,表示它在const成员函数中也可以被修改(常用于缓存、互斥锁等场景)。const_cast运算符:用于移除对象的const或volatile属性。极其危险,除非你确切知道被转换的原始对象本身不是常量(例如,向接收非const指针的旧式 C 接口传递数据时)。constexpr(C++11):比const更强大,用于定义编译期常量,并要求常量表达式能在编译时求值。可用于变量、函数、构造函数等。constexpr函数如果传入编译期常量,则结果也是编译期常量。- 与
volatile的区别:const告诉编译器“不要变”,volatile告诉编译器“可能会意外改变”(防止优化,常用于硬件映射内存)。
4 面试回答思路
回答时可以按层次展开:
- 定性:
const是承诺不变性,由编译器保证。 - 分场景:从变量 -> 指针/引用 -> 函数参数 -> 成员函数 -> 返回值,逐一说明。
- 点出关键:
- 指针的
const在*左/右的区别。 - 常量引用的绑定特性。
const成员函数的双重意义(不修改对象、使const对象可调用)。- 顶层/底层
const的概念。
- 指针的
- 提及周边:简单提一下
mutable、const_cast和constexpr的作用与注意事项。
掌握这些,就掌握了 C++const的绝大部分“八股”内容。理解其背后的“承诺与保证”思想,比死记硬背语法更重要。