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