const 全面梳理

发布时间:2026/7/6 14:37:53
const 全面梳理 const 全面梳理面试题 1const int *p与int *const p的区别这是 C 面试出场率最高的 const 问题考的是顶层 consttop-level const与底层 constlow-level const的区分。#includememory#includeiostreamintmain(){inta10,b20;constint*p1a;// 底层 const不能通过 p1 修改指向的值// *p1 30; // ❌ 编译错误p1b;// ✅ 可以改变指向int*constp2a;// 顶层 constp2 本身不可变*p230;// ✅ 可以通过 p2 修改值// p2 b; // ❌ 编译错误constint*constp3a;// 顶层 底层既不能改指向也不能改值std::cout*p1 *p2 *p3\n;}记忆口诀const 在*左边是底层值不变在*右边是顶层指针本身不变。面试题 2const 成员函数与 mutable 的用途const 成员函数在成员函数参数列表后加constthis 指针的类型从T* const变为const T* const——即 this 指向的对象变为 const函数内不能修改非 mutable 成员变量。#includeiostreamclassWidget{public:explicitWidget(intv):data_(v){}intget()const{// const 成员函数// data_ 42; // ❌ 编译错误returndata_;}voidset(intv){// 非 const 成员函数data_v;}private:intdata_;};voidprint(constWidgetw){// const 引用只能调用 const 成员函数std::coutw.get()\n;// w.set(10); // ❌ 编译错误}mutable 与逻辑 const vs 位 const位 constbitwise const编译器层面的检查——成员变量的每一个二进制位都不能变。逻辑 constlogical const语义上不改变对象的外部可见状态但内部实现可能需要修改。mutable用于打破位 const 约束在 const 成员函数中修改那些不影响对象外部状态的成员#includeiostream#includemutex#includethreadclassCounter{public:explicitCounter(intv):value_(v){}intget()const{std::lock_guardstd::mutexlock(mutex_);// mutable 互斥锁returnvalue_;}voidinc(){std::lock_guardstd::mutexlock(mutex_);value_;}private:intvalue_;mutablestd::mutex mutex_;// 互斥锁const 成员函数也需要加锁mutablesize_t cache_hits_0;// 缓存命中统计不影响对象语义};典型 mutable 场景引用计数如std::shared_ptr的控制块缓存懒求值后缓存结果互斥锁const 访问也需要线程安全调试计数器调用次数统计面试题 3constexpr 与编译期求值constexpr从 C11 开始引入逐代放宽编译期求值能力标准能力C11函数体只能有一条return语句不能有循环/分支/局部变量C14可用循环、分支、局部变量、多语句C17if constexpr编译期条件分支C20constexpr virtual函数、constexpr动态分配编译期new/delete#includeiostream#includearray// C14 constexpr可在函数内使用循环constexprintfactorial(intn){intresult1;for(inti2;in;i)result*i;returnresult;}// C17 if constexpr编译期分支消除templatetypenameTconstexprautotype_desc(){ifconstexpr(std::is_integral_vT)returninteger;elseifconstexpr(std::is_floating_point_vT)returnfloating point;elsereturnother;}intmain(){// 编译期求值不产生运行时开销constexprintfac10factorial(10);std::arrayint,fac10arr{};// 编译期数组大小std::cout10! fac10\n;std::couttype_descint()\n;// 也可以在运行时上下文中调用若参数不是 constexprintn5;std::coutfactorial(n)\n;// 运行时求值}constexpr变量必须编译期初始化这是与普通const的关键区别intrand_val();// 运行时函数constintarand_val();// ✅ 运行期 constconstexprintbrand_val();// ❌ 编译错误面试题 4consteval / constinitC20consteval — 立即函数consteval强制函数必须在编译期求值不允许退化为运行时调用。#includeiostreamconstevalintsquare(intx){returnx*x;}intmain(){constexprintasquare(5);// ✅ 编译期// int b square(rand()); // ❌ 编译错误rand() 不是常量表达式// consteval 函数可以出现在需要常量表达式的位置std::arrayint,square(3)arr;// ✅ size 9std::coutarr.size()\n;}constinit — 静态初始化保证constinit保证变量在静态初始化阶段完成初始化避免 dynamic init 的初始化顺序问题static initialization order fiasco。#includeiostream#includerandom// 文件作用域的非局部变量初始化顺序在 TUs 之间是未定义的// int g_counter some_function(); // ❌ 可能在其他 TU 需要的变量之前未初始化// constinit 保证编译期或静态初始化constinitintg_const_val42;// ✅ 常量初始化静态初始化constinitstd::ios_base::Init g_ios_init;// ✅ 保证 iostream 在 main 前就绪// constinit 变量必须静态初始化// constinit std::mt19937 g_rng{std::random_device{}()}; // ❌ 编译错误不是常量初始化intmain(){std::coutg_const_val\n;}关键字初始化时机可修改使用场景const编译或运行期否运行时不变量constexpr编译期否编译期常量consteval编译期调用—立即函数constinit静态初始化是全局变量面试题 5逻辑 const 与位 const 的区别这是面试中区分能不能用和该不该用的关键。#includeiostream#includecstringclassStringWrapper{public:explicitStringWrapper(constchar*s):len_(std::strlen(s)){data_newchar[len_1];std::memcpy(data_,s,len_1);}~StringWrapper(){delete[]data_;}constchar*c_str()const{// 满足位 constreturndata_;}charoperator[](size_t idx){returndata_[idx];}constcharoperator[](size_t idx)const{returndata_[idx];// 位 const 通过成员指针值没变}// ⚠️ 错误示范const 成员函数返回内部指针外部可以修改内部状态char*bad_get()const{returndata_;// ❌ 逻辑 const 被破坏}private:char*data_;// 指针本身不变但指向的内容可能变size_t len_;};intmain(){constStringWrapperstr(hello);// str[0] H; // ❌ 编译错误const 版本返回 const charchar*pstr.bad_get();// ⚠️ 编译通过p[0]H;// ⚠️ 修改了 const 对象的内部数据std::coutstr.c_str()\n;// Hello — 逻辑 const 被破坏}位 const编译器检查——所有成员变量的二进制值不变。bad_get()返回指针时指针值没变编译通过。逻辑 const语义检查——对象外部可见状态不应改变。bad_get()让外部获得写权限破坏了逻辑 const。const 使用场景总览const 用于哪里修饰变量修饰指针修饰引用修饰成员函数修饰返回值const int x 5;不可变绑定const int *p底层 const值不变int *const p顶层 const指向不变const int *const p双重 constconst T延长临时对象生命周期const T 做参数避免拷贝 禁止修改位 const编译器检查逻辑 constmutable 打破const T 返回值防止修改临时对象综合示例面试中一口气展示 const 六种用法#includeiostream#includestring#includevectorclassDatabase{public:explicitDatabase(std::string name):name_(std::move(name)){}// 1. const 成员函数承诺不修改对象std::stringname()const{std::lock_guardstd::mutexlock(mutex_);query_count_;returnname_;}// 2. const T 参数 底层 const 指针voidbatch_insert(conststd::vectorintvalues){for(constint*pvalues.data();p!values.data()values.size();p)data_.push_back(*p);}// 3. const T 返回值防止 user db.get() 后修改临时对象conststd::vectorintdata()const{returndata_;}// 4. constexpr 静态工厂staticconstexprDatabasedefault_db(){returnDatabase{default};}private:std::string name_;std::vectorintdata_;mutablestd::mutex mutex_;mutablesize_t query_count_0;};intmain(){constexprautodbDatabase::default_db();constDatabaserefdb;// const 引用延长生命周期std::coutref.name()\n;ref.batch_insert({1,2,3});// 非 const 成员通过 const 引用不能调用// 实际上 batch_insert 不是 const这里只是演示设计意图}面试要点小结问题一句话答案顶层/底层 const*左是底层指向的值*右是顶层指针自身const 成员函数this变const T*不能修改对象mutable 除外mutable打破位 const用于互斥锁/缓存/统计constexpr vs constconstexpr 保证编译期求值consteval必须编译期不允许退化为运行时constinit保证全局变量在 main 前完成静态初始化逻辑 const vs 位 const位 const 编译器二进制检查逻辑 const 语义不变性