【C++】零基础入门 · 第 12 节:引用与 const 限定符

【C++】零基础入门 · 第 12 节:引用与 const 限定符 前面我们学了指针——C 中最强大也最容易出错的特性。这一节我们来学两个让代码更安全、更易读的工具引用和const 限定符。1. 引用Reference1.1 什么是引用引用是变量的「别名」——给一个已存在的变量取另一个名字。对引用的操作就是对原变量的操作。inta10;intba;// b 是 a 的引用别名b20;// 修改 b就是修改 acouta;// 输出 20关键点引用必须在声明时初始化引用一旦绑定到某个变量就不能再绑定到其他变量引用不是新变量它不占用额外的存储空间概念上1.2 引用 vs 指针引用和指针都能间接访问变量但有本质区别inta10;int*pa;// 指针需要取地址需要解引用intra;// 引用直接用语法更简洁*p20;// 指针需要 * 来解引用r30;// 引用直接用就像原变量一样特性引用指针初始化必须在声明时初始化可以不初始化但不推荐可否为空不能为 null可以为 null可否改变指向不能可以语法和原变量一样需要*和安全性更安全容易出错简单规则能用引用就不用指针。1.3 引用作为函数参数引用最常见的用途是作为函数参数实现「传引用」// 传值函数内修改不影响外部voidswapBad(inta,intb){inttempa;ab;btemp;// 外部的 a 和 b 不会变}// 传引用函数内修改影响外部voidswapGood(inta,intb){inttempa;ab;btemp;// 外部的 a 和 b 交换了}intmain(){intx10,y20;swapGood(x,y);coutx y;// 输出20 10}1.4 引用作为函数返回值函数可以返回引用但要注意不要返回局部变量的引用// ❌ 错误返回局部变量的引用intbadFunction(){intlocal42;returnlocal;// local 在函数结束后被销毁引用变成悬空引用}// ✅ 正确返回全局变量或静态变量的引用intglobalVar100;intgetGlobal(){returnglobalVar;// 全局变量不会被销毁}// ✅ 正确返回参数的引用intgetElement(vectorintarr,intindex){returnarr[index];// 数组元素在调用者的作用域中}返回引用的函数调用可以出现在赋值号左边vectorintarr{1,2,3,4,5};getElement(arr,2)99;// arr[2] 变成 99coutarr[2];// 输出 991.5 const 引用用const修饰引用表示「只读」voidprintValue(constintx){// x 100; // ❌ 编译错误不能修改 const 引用coutx;// ✅ 只能读取}intmain(){inta42;printValue(a);// 正常传入printValue(100);// 也可以传入字面量// 普通引用不能绑定到字面量const 引用可以}const 引用的好处防止函数意外修改参数避免拷贝提高效率可以绑定到字面量和临时对象2. const 限定符2.1 const 变量const表示「常量」一旦初始化就不能修改constintMAX_SIZE100;// MAX_SIZE 200; // ❌ 编译错误conststring NAMEAlice;// NAME Bob; // ❌ 编译错误const 变量必须在声明时初始化因为之后无法赋值。2.2 const 与指针const 和指针的组合是初学者最容易混淆的地方。记住一个口诀const 在*左边指向的内容不能改const 在*右边指针本身不能改。inta10,b20;// 1. 指向 const 的指针内容只读constint*p1a;// *p1 30; // ❌ 不能通过 p1 修改 ap1b;// ✅ p1 可以指向其他变量a30;// ✅ 直接修改 a 是可以的// 2. const 指针指针本身只读int*constp2a;*p230;// ✅ 可以通过 p2 修改 a// p2 b; // ❌ p2 不能指向其他变量// 3. 指向 const 的 const 指针都不能改constint*constp3a;// *p3 30; // ❌// p3 b; // ❌记忆方法const int*const 修饰 int所以 int 不能改 → 内容只读int* constconst 修饰int*所以指针不能改 → 指针只读2.3 const 成员函数在类中const 放在函数声明末尾表示这个函数不会修改对象的状态classCircle{private:doubleradius;public:Circle(doubler):radius(r){}// const 成员函数不会修改对象doublegetArea()const{// radius 10; // ❌ 不能在 const 函数中修改成员变量return3.14159*radius*radius;}// 非 const 成员函数可以修改对象voidsetRadius(doubler){radiusr;}};voidprintArea(constCirclec){// c 只能调用 const 成员函数coutc.getArea()endl;// c.setRadius(5); // ❌ 不能调用非 const 函数}规则如果一个函数不修改对象状态就把它声明为const。这样它就能被const引用和const指针调用。2.4 常量表达式constexprC11 引入了constexpr比const更严格constintx10;// x 是常量constexprinty20;// y 是编译期常量constintzgetSomeValue();// ✅ 运行时确定的常量也可以// constexpr int w getSomeValue(); // ❌ 必须编译期能确定constexprintsquare(intn){returnn*n;}constexprintresultsquare(5);// 编译期计算result 25constexpr告诉编译器这个值在编译期就能确定可以做更多优化。2.5 mutable 关键字有时候你需要在 const 成员函数中修改某些特定的成员变量比如缓存、计数器。mutable就是为此设计的classDataFetcher{private:mutableintaccessCount0;// 即使在 const 函数中也能修改string data;public:stringgetData()const{accessCount;// ✅ mutable 变量可以在 const 函数中修改returndata;}intgetAccessCount()const{returnaccessCount;}};3. 引用与 const 的实际应用3.1 函数参数的最佳实践// ✅ 最佳实践按需选择参数传递方式// 基本类型int、double 等传值voidprocessInt(intx){/* ... */}// 不需要修改的大对象const 引用voidprintVector(constvectorintvec){for(intx:vec)coutx ;}// 需要修改的参数引用voidsortVector(vectorintvec){sort(vec.begin(),vec.end());}// 可能为空的参数指针voidprocessMaybeNull(int*ptr){if(ptr!nullptr){// 处理数据}}3.2 返回值的最佳实践classMatrix{private:vectorvectorintdata;public:// 返回 const 引用只读访问constvectorintgetRow(inti)const{returndata[i];}// 返回非 const 引用可修改访问vectorintgetRow(inti){returndata[i];}// 返回值小对象或需要返回新对象时intgetElement(inti,intj)const{returndata[i][j];}};3.3 范围 for 循环中的引用vectorintarr{1,2,3,4,5};// 只读遍历const 引用for(constintx:arr){coutx ;// 高效不拷贝}// 修改遍历非 const 引用for(intx:arr){x*2;// 原地修改每个元素}// 不用引用会拷贝每个元素对于大对象效率低for(intx:arr){coutx ;// 基本类型没问题string 等大对象会有额外拷贝}4. 常见陷阱4.1 悬空引用intgetDangling(){intlocal42;returnlocal;// ⚠️ 返回局部变量的引用}intmain(){intrefgetDangling();coutref;// 未定义行为可能输出垃圾值}4.2 const 丢失constinta10;constint*pa;// int* bad p; // ❌ 编译错误不能丢弃 constint*badconst_castint*(p);// ⚠️ 强制转换但修改 a 是未定义行为4.3 引用的引用inta10;intr1a;intr2r1;// r2 是 a 的引用不是 r1 的引用r220;couta;// 输出 205. 小结本节我们学习了引用和 const 限定符引用是变量的别名必须初始化不能重新绑定const 引用可以绑定到字面量防止意外修改const 指针有两种指向内容的 const 和指针本身的 constconst 成员函数表示不修改对象状态constexpr是编译期常量比 const 更严格最佳实践函数参数优先用const 引用只读或引用需修改不修改对象的成员函数都声明为const能用引用就不用指针用constexpr代替const定义编译期常量下一节我们将学习类与对象基础——C 面向对象编程的起点。