C++20 高级编程 002:夯实现代C++底层基本功
- 前言
- Bilibili 同步视频
- 一、告别头文件混乱:C++20 Module 模块系统
- 核心规则
- 完整可运行示例
- 二、分支控制全解:if/else、switch 双巨头,C++20 新增初始化语法
- 1. if/else:级联判断 + 内置初始化器
- 2. switch 语句:枚举匹配、穿透处理、同样支持初始化
- 高频坑:case 穿透 fallthrough
- 三、简洁万能表达式:三元条件运算符?:
- 四、逻辑与比较运算符:短路求值 + 太空飞船 <=> 运算符
- 1. 基础运算符清单 & 短路特性
- 2. C++20 王牌:三向比较运算符 <=>(太空飞船运算符)
- 五、现代 C++ 函数体系:自动返回值、重载、编译期内置变量
- 1. 基础声明与定义
- 2. auto 自动推导返回类型
- 3. **func** 获取当前函数名
- 4. 函数重载
- 六、编译器属性 [[xxx]]:给编译器传递提示,规避警告、优化分支
- 七、数组选型:摒弃老旧 C 数组,优先 std::array
- 1. C 风格原生数组痛点
- 2. std::array 标准固定数组容器(推荐首选)
- 结语
前言
不少刚上手现代 C++ 的开发者,还停留在 C 语言式的老旧写法里,既错过 C++20 带来的便捷新特性,又容易踩各种隐性 bug。今天我们把 C++20 最核心的入门语法串讲一遍,涵盖模块化编程、各类分支控制、新式运算符、函数设计、编译属性、数组容器六大板块,搭配可直接运行的示例代码,轻松吃透现代 C++ 基础语法,避开无数新手高频坑✨
Bilibili 同步视频
C++20 高级编程 002:夯实现代C++底层基本功
一、告别头文件混乱:C++20 Module 模块系统
在 C++20 之前,项目依赖.h头文件反复包含,宏污染、重复引入、编译缓慢都是家常便饭。而模块(module)彻底重构了代码导入导出逻辑,核心靠export控制对外可见内容。
核心规则
export module xxx;定义独立模块;export修饰结构体、函数,外部导入后才能访问;不加export仅模块内部可用;导入标准库用尖括号
import <xxx>,导入自定义模块禁止尖括号,直接写模块名。
完整可运行示例
// employee.ixx 模块接口文件exportmoduleemployee;// 导出结构体,外部模块可直接使用exportstructEmployee{charfirstInitial;charlastInitial;intemployeeNumber;intsalary;};// main.cppimport<iostream>;import<format>;importemployee;usingnamespacestd;intmain(){// 实例化员工结构体并赋值Employee staff{};staff.firstInitial='L';staff.lastInitial='W';staff.employeeNumber=1001;staff.salary=150000;// format格式化输出,可读性远优于老式printfcout<<format("员工简称:{}{}n",staff.firstInitial,staff.lastInitial);cout<<format("工号:{}n",staff.employeeNumber);cout<<format("年薪:${}n",staff.salary);return0;}对比传统头文件,模块不会产生全局污染,编译速度大幅提升,大型工程维护体验直接拉满。
二、分支控制全解:if/else、switch 双巨头,C++20 新增初始化语法
业务逻辑离不开条件判断,C++ 提供两套分支体系,同时新增初始化器语法,大幅精简代码层级。
1. if/else:级联判断 + 内置初始化器
普通 if-else if-else 逻辑无需多讲,记住一条铁律:表达式中0 等价 false,非 0 全部视为 true。
而 C++17 起支持if(初始化语句; 判断条件),初始化的变量生命周期仅限整条 if 分支,局部性更强,减少无用变量污染全局作用域。
示例场景:读取员工信息,同时判断薪资阈值
// 复用上方Employee结构体intgetEmployee(){return9000;}intmain(){// if内置初始化:临时获取薪资,仅分支内可用if(intsalary=getEmployee();salary>10000){cout<<format("高薪员工,薪资:{}n",salary);}elseif(salary>5000){cout<<format("普通薪资,薪资:{}n",salary);}else{cout<<format("基础薪资,薪资:{}n",salary);}// 此处无法访问salary,变量生命周期已结束return0;}2. switch 语句:枚举匹配、穿透处理、同样支持初始化
switch 匹配类型受限:仅整型、可转整型类型、枚举、强枚举,只能和常量对比。
高频坑:case 穿透 fallthrough
如果 case 末尾不写break,代码会顺序执行下一个 case 逻辑,绝大多数场景是 bug;但如果是刻意穿透,必须加[[fallthrough]]属性消除编译器警告。
enumclassMode{Custom,Standard,Default};intmain(){Mode runMode=Mode::Custom;intvalue=0;switch(runMode){usingenumMode;caseCustom:value=84;// 刻意穿透,标记属性避免告警[[fallthrough]];caseStandard:caseDefault:cout<<format("当前配置数值:{}n",value);break;default:cout<<"未知运行模式n";break;}return0;}另外 switch 同样支持初始化语法:switch(变量初始化; 匹配表达式),适合读取配置后直接分支判断。
三、简洁万能表达式:三元条件运算符?:
if 语句是语句,不能嵌入赋值、打印等表达式场景;而三元运算符是纯正表达式,随时随地可用,语法模板:判断条件 ? 条件成立取值 : 条件不成立取值
intnum=10;// 直接嵌入cout输出,代码极度精简cout<<(num>5?"数字大于5":"数字小于等于5")<<endl;// 用于赋值场景std::string desc=num%2==0?"偶数":"奇数";cout<<desc<<endl;小技巧:问号可以理解为提问,num>5?翻译为 “num 大于 5 吗?”,记忆零难度。
四、逻辑与比较运算符:短路求值 + 太空飞船 <=> 运算符
1. 基础运算符清单 & 短路特性
| 运算符 | 作用 | 关键注意点 | |
|---|---|---|---|
== | 判断相等 | 切勿和赋值=混淆,新手高频 bug | |
!= | 不等于 | - | |
< <= > >= | 大小比较 | - | |
! | 逻辑非(取反) | 一元运算符,单操作数 | |
&& | 逻辑与 | 左侧 false 直接短路,右侧不执行 | |
| ` | ` | 逻辑或 | 左侧 true 直接短路,右侧不执行 |
短路是一把双刃剑👍:
优势:做指针判空、边界校验,避免非法访问;减少多余判断,提升运行效率;
隐患:若右侧包含函数调用、变量自增,短路后代码不会执行,隐藏难以复现的隐性 bug。
2. C++20 王牌:三向比较运算符 <=>(太空飞船运算符)
传统写法判断两个数字大小,至少需要两次比较;<=>一次运算就能区分小于、等于、大于三种关系,返回标准化排序类型。
整数:
std::strong_ordering(less / equal / greater)浮点数:
std::partial_ordering(存在 unordered,对应 NaN 无效数值)
配套<compare>头文件工具函数is_eq/is_lt/is_gt,简化结果判断:
#include<compare>#include<iostream>intmain(){inta=11,b=5;autores=a<=>b;if(std::is_lt(res))cout<<"a < bn";elseif(std::is_eq(res))cout<<"a == bn";elseif(std::is_gt(res))cout<<"a > bn";return0;}大型自定义类对比场景优势巨大:仅执行一次比较逻辑,代替两次昂贵的成员比对,性能收益显著。
五、现代 C++ 函数体系:自动返回值、重载、编译期内置变量
函数是代码拆分的核心单元,C++ 在 C 基础上拓展了大量实用特性。
1. 基础声明与定义
声明(函数原型)仅描述调用规则,定义携带函数实体;无返回值标注void,无参数直接空括号,无需写void(和 C 区分)。
2. auto 自动推导返回类型
不用手动写返回值,编译器根据return表达式自动推导;多 return 语句返回类型必须统一。
// 自动推导返回值为intautoadd(inta,intb){returna+b;}3.func获取当前函数名
内置局部变量,快速实现日志埋点,调试神器:
autoadd(inta,intb){std::cout<<format("进入函数:{}n",__func__);returna+b;}4. 函数重载
同名函数依靠参数数量 / 参数类型区分,仅返回值不同无法重载,编译器会根据实参自动匹配版本:
// 整数重载intcalc(inta,intb){returna+b;}// 浮点重载doublecalc(doublea,doubleb){returna+b;}六、编译器属性 [[xxx]]:给编译器传递提示,规避警告、优化分支
C++11 标准化双方括号属性,用来给编译器附加额外提示,日常开发最常用 5 种:
- [[nodiscard]]
修饰带返回值函数,调用时忽略返回值会触发编译警告,C++20 支持附加说明文字,专门用来防止遗漏错误码:
[[nodiscard("返回错误码,不可忽略")]]intcheckAuth(){return1;}intmain(){checkAuth();// 编译器抛出警告,提醒丢弃返回值return0;}- [[maybe_unused]]
屏蔽 “变量 / 参数未使用” 警告,接口预留占位参数必备:
voidtest(inta,[[maybe_unused]]intreserveParam){cout<<a<<endl;}- [[noreturn]]
标记函数永远不会返回调用方(程序退出、抛出异常),消除 “分支无返回值” 编译报错:
#include<cstdlib>[[noreturn]]voidexitApp(){std::exit(-1);}[[deprecated (“替换为 newFunc 接口”)]]
标记接口废弃,调用时告警,用于版本迭代淘汰旧 API。[[likely]] / [[unlikely]]
标记高 / 低概率分支,辅助编译器做底层指令优化,仅极致性能场景使用。
七、数组选型:摒弃老旧 C 数组,优先 std::array
1. C 风格原生数组痛点
长度必须是常量,不支持变量动态长度;
下标从 0 开始,极易越界;
隐式转为普通指针,丢失长度信息,传递后无法获取大小;
初始化示例:
intarr[5]{};// 全部初始化为0intarr2[]{1,2,3};// 编译器自动推导长度为3获取长度两种方式:
#include<array>intarr[5]{};size_t len1=std::size(arr);// 现代标准推荐size_t len2=sizeof(arr)/sizeof(arr[0]);// 老式写法2. std::array 标准固定数组容器(推荐首选)
定义于<array>,对 C 数组轻量封装,解决原生数组全部缺陷:自带 size ()、不会隐式转指针、支持迭代器,模板格式std::array<元素类型, 固定长度>
#include<array>#include<iostream>#include<format>intmain(){std::array<int,4>nums{10,20,30,40};cout<<format("容器长度:{}n",nums.size());cout<<format("第二个元素:{}n",nums[1]);return0;}结语
以上覆盖了 C++20 入门阶段最核心的语法体系,从模块化工程管理、分支逻辑、新式运算符,到函数规范、编译属性、容器数组,每一块都配套可直接编译运行的代码。
现代 C++ 早已摆脱 C 语言粗糙的语法束缚,合理运用模块、初始化分支、<=>运算符、std::array 等新特性,既能减少 80% 新手 bug,又能提升代码可读性与运行性能。后续我们可以基于这些基础,继续深挖类、模板、标准容器等进阶内容,逐步构建完整的现代 C++ 知识栈。