C++20 高级编程 003:吃透现代基础语法与标准容器

发布时间:2026/8/19 9:09:33
C++20 高级编程 003:吃透现代基础语法与标准容器 C20 高级编程 003吃透现代基础语法与标准容器前言Bilibili 同步视频一、告别裸 C 数组std::array 定长容器完胜传统数组1. 原生 C 数组的先天缺陷痛点 1获取长度方式丑陋且有局限痛点 2多维数组可读性差、无内置长度接口2. std::array编译期定长安全容器完整示例代码二、动态万能容器 std::vector运行时自由伸缩实操代码演示三、两大实用工具模板pair optional3.1 std::pair打包两个不同类型数据3.2 std::optional (C17)优雅处理 “空值” 场景完整示例代码四、C17 结构化绑定一键拆解复合数据三段场景代码五、四种循环全解C20 范围 for 新增初始化语法全套演示代码六、initializer_list实现可变参数函数示例实现多数字求和工具七、std::string 现代字符串替代 C 字符数组八、C 面向对象基础封装思想落地实践关键概念梳理模块化完整代码示例C20 module 新特性airline_ticket.cppm 模块接口文件main.cpp 使用模块作用域解析运算符避坑指南九、统一初始化 {} C20 指派初始化器根治类型截断 bug9.1 统一初始化C11 核心特性对比演示9.2 C20 指派初始化器聚合类型专属结尾总结前言不少刚从 C 语言转场 C 的开发者写代码时总带着浓重的 “C 味”裸数组满天飞、手动处理指针边界、靠特殊数值标记空返回值…… 代码臃肿、隐藏着大量越界、类型截断、野指针隐患。C11 至 C20 迭代新增了大量现代化语法、安全容器与工具类从根源规避传统写法的各类坑。今天我们就从零梳理一套规范、优雅、低 bug 的现代 C 基础体系覆盖容器、语法糖、面向对象、内存初始化四大核心板块附带可直接运行的实操代码帮大家彻底抛弃过时的 C 风格编码习惯✨Bilibili 同步视频C20 高级编程 003吃透现代基础语法与标准容器一、告别裸 C 数组std::array 定长容器完胜传统数组1. 原生 C 数组的先天缺陷传统栈数组写法简单但处处暗藏陷阱// C风格数组初始化仅首个元素赋值其余自动置0intmyArray[3]{2};痛点 1获取长度方式丑陋且有局限老式依靠sizeof计算元素数量仅栈数组可用数组退化为指针后直接失效// 老式写法size_t arraySizesizeof(myArray)/sizeof(myArray[0]);// C17标准写法推荐array头文件size_t arraySizestd::size(myArray);痛点 2多维数组可读性差、无内置长度接口以井字棋棋盘二维数组举例访问逻辑晦涩没有自带 size 接口charticTacToeBoard[3][3];ticTacToeBoard[1][1]o;// 棋盘中心填充字符官方明确建议工程开发尽量规避 C 风格数组优先选用std::array。2. std::array编译期定长安全容器std::array定义于array本质是对原生数组的轻量化封装编译期固定容量运行时无法扩容对比裸数组优势拉满✅ 自带.size()接口随时获取元素总数✅ 不会隐式转换为裸指针杜绝数组退化 bug✅ 完整支持迭代器、范围 for 遍历✅ 支持 C17 CTAD 类模板参数推导省略冗余模板参数完整示例代码#includearray#includeformat#includeiostreamintmain(){// 传统完整模板写法std::arrayint,3arr{9,8,7};std::coutstd::format(容器总长度{}n,arr.size());std::coutstd::format(第二个元素{}n,arr[1]);// CTAD自动推导极简写法无需填写类型,长度std::array arr2{10,20,30,40};std::coutstd::format(CTAD数组长度{}n,arr2.size());return0;}关键区分std::array容量编译锁定如果需要运行时动态增减元素请看下方std::vector。二、动态万能容器 std::vector运行时自由伸缩std::vector在vector头文件是 C 最常用动态数组自动管理堆内存无需手动 new/delete完美适配长度不确定的业务场景。push_back()尾部追加元素自动扩容内存支持下标[]随机访问同样支持 CTAD 推导但空声明不能省略模板参数实操代码演示#includevector#includeformat#includeiostreamintmain(){// 初始化创建容器填充两个11std::vectorintmyVector{11,22};// 动态追加元素myVector.push_back(33);myVector.push_back(44);// CTAD简化写法std::vector vec{100,200,300};std::coutstd::format(vector首个元素{}n,myVector[0]);return0;}⚠️ 踩坑提醒vector vec;直接空定义会编译报错CTAD 必须依靠初始化列表推导类型。三、两大实用工具模板pair optional3.1 std::pair打包两个不同类型数据utility头文件下的std::pair专门用于捆绑两组异构数据通过.first、.second访问成员CTAD 加持简化代码#includeutility#includeformat#includeiostreamintmain(){std::pairdouble,intmyPair{1.23,5};// CTAD极简写法std::pair pairDemo{3.14,666};std::coutstd::format(first值{}second值{}n,myPair.first,myPair.second);return0;}3.2 std::optional (C17)优雅处理 “空值” 场景以往我们靠-1、nullptr、EOF这类特殊值标记函数无返回值可读性差、极易误判。std::optionalT完美解决它只有两种状态持有有效值 / 空。核心 APIhas_value()布尔判断是否存在有效值.value()取值空状态直接抛异常std::bad_optional_access*opt解引用取值和 value 行为一致.value_or(val)空值时返回自定义兜底值无异常风险完整示例代码#includeoptional#includeformat#includeiostream// 可能返回有效值也可能返回空std::optionalintgetData(boolgiveIt){if(giveIt)return42;returnstd::nullopt;}intmain(){std::optionalintdata1getData(true);std::optionalintdata2getData(false);std::coutstd::format(data1是否有值{}n,data1.has_value());// 直接if判断optionalif(!data2)std::coutdata2为空无存储数据n;std::coutstd::format(data1取值{}、解引用取值{}n,data1.value(),*data1);// 空值兜底返回0std::coutstd::format(data2兜底取值{}n,data2.value_or(0));return0;}限制说明optionalT非法无法存储引用但可以存放指针。四、C17 结构化绑定一键拆解复合数据auto [变量列表]结构化绑定堪称代码清爽神器数组、pair、自定义聚合结构体都能一键拆分为独立变量大幅简化取值逻辑。硬性规则必须搭配auto不能替换 int/string 等具体类型变量数量必须和容器 / 结构体成员数量严格匹配auto可绑定引用直接修改原数据const auto只读不拷贝三段场景代码#includearray#includeutility#includeformat#includeiostream// 自定义聚合结构体structPoint{doublem_x,m_y,m_z;};intmain(){// 1. 拆解std::arraystd::array arr{11,22,33};auto[x,y,z]arr;std::coutstd::format(数组拆解{} {} {}n,x,y,z);// 2. 拆解pairstd::pair strIntPair{hello,99};auto[strVal,intVal]strIntPair;std::coutstd::format(pair拆解{} {}n,strVal,intVal);// 3. 拆解自定义结构体Point point{1.0,2.0,3.0};auto[px,py,pz]point;std::coutstd::format(坐标点{} {} {}n,px,py,pz);return0;}五、四种循环全解C20 范围 for 新增初始化语法C 提供四类循环适配不同业务场景其中基于范围的 for 循环是遍历容器首选简洁干净无下标越界风险。while先判断条件满足再执行do-while先执行一次再判断条件至少运行 1 次for初始化、终止条件、迭代更新集中一行计数循环专用基于范围 for遍历数组 / 容器C20 新增循环内初始化器全套演示代码#includearray#includeiostreamintmain(){// 1. while循环inti{0};while(i3){std::coutwhile循环执行n;i;}// 2. do-while 必定执行一次intj{100};do{std::coutdo-while强制执行一次n;j;}while(j3);// 3. 传统for计数循环for(intk{0};k3;k){std::cout传统for循环n;}// 4. 基础范围for循环std::array arr{1,2,3,4};for(intval:arr){std::coutval ;}std::coutn;// C20 范围for内置初始化器语法for(std::array temp{10,20,30};intnum:temp){std::coutnum ;}return0;}六、initializer_list实现可变参数函数initializer_list提供类型安全的可变参数列表函数可接收任意数量同类型参数编译器强制校验元素类型杜绝类型混乱。示例实现多数字求和工具#includeinitializer_list#includeiostreamintmakeSum(std::initializer_listintvalues){inttotal{0};for(intv:values)totalv;returntotal;}intmain(){intamakeSum({1,2,3});intbmakeSum({10,20,30,40});std::coutstd::format(求和1{}求和2{}n,a,b);// makeSum({1, 2, 3.14}); 编译报错禁止double混入int列表return0;}七、std::string 现代字符串替代 C 字符数组C 语言依靠char[]实现字符串操作繁琐、极易缓冲区溢出C 标准库std::string封装完整字符串能力使用逻辑和基础类型一致支持下标随机访问定义于string头文件#includestring#includeformat#includeiostreamintmain(){std::string myStr{Hello Modern C20};std::coutstd::format(完整字符串{}n,myStr);std::coutstd::format(第二个字符{}n,myStr[1]);return0;}八、C 面向对象基础封装思想落地实践C 是完全面向对象语言核心封装准则所有数据成员私有对外仅开放 get/set 读写接口隔离内部数据与外部调用后续修改内部存储逻辑无需改动上层业务代码。关键概念梳理访问限定符public外部可自由调用存放对外接口private仅类内部访问存储数据成员统一加m_前缀区分protected继承场景专用子类可访问构造函数与类同名、无返回值创建对象自动调用析构函数~类名对象销毁自动执行用于释放文件、堆内存等资源三种成员初始化方式构造初始化列表 类内就地初始化 构造函数内赋值模块化完整代码示例C20 module 新特性airline_ticket.cppm 模块接口文件exportmoduleairline_ticket;importstring;importformat;exportclassAirlineTicket{public:// 构造、析构声明AirlineTicket();~AirlineTicket()default;// 对外接口doublecalculatePriceInDollars();std::stringgetPassengerName();voidsetPassengerName(std::string name);intgetNumberOfMiles();voidsetNumberOfMiles(intmiles);boolhasEliteSuperRewardsStatus();voidsetHasEliteSuperRewardsStatus(boolstatus);private:// 类内就地初始化std::string m_passengerName{Unknown Passenger};intm_numberOfMiles{0};boolm_hasEliteSuperRewardsStatus{false};};// 类内直接实现方法doubleAirlineTicket::calculatePriceInDollars(){if(hasEliteSuperRewardsStatus())return0.0;returngetNumberOfMiles()*0.1;}std::stringAirlineTicket::getPassengerName(){returnm_passengerName;}voidAirlineTicket::setPassengerName(std::string name){m_passengerNamestd::move(name);}intAirlineTicket::getNumberOfMiles(){returnm_numberOfMiles;}voidAirlineTicket::setNumberOfMiles(intmiles){m_numberOfMilesmiles;}boolAirlineTicket::hasEliteSuperRewardsStatus(){returnm_hasEliteSuperRewardsStatus;}voidAirlineTicket::setHasEliteSuperRewardsStatus(boolstatus){m_hasEliteSuperRewardsStatusstatus;}// 构造函数初始化列表写法AirlineTicket::AirlineTicket():m_passengerName(Unknown Passenger),m_numberOfMiles(0),m_hasEliteSuperRewardsStatus(false){}main.cpp 使用模块importairline_ticket;importformat;importiostream;intmain(){AirlineTicket ticket;ticket.setPassengerName(Sherman T. Socketwrench);ticket.setNumberOfMiles(700);doublecostticket.calculatePriceInDollars();std::coutstd::format(机票价格${}n,cost);return0;}作用域解析运算符避坑指南代码中同名变量、函数随处可见依靠::区分不同作用域命名空间::函数指定命名空间内接口::函数名强制调用全局域同名函数#includeiostreamclassDemo{public:intget(){return5;}};// 全局get函数intget(){return10;}namespaceNS{intget(){return20;}}intmain(){Demo d;std::coutd.get()n;// 输出5 类内方法std::coutNS::get()n;// 输出20 命名空间内std::cout::get()n;// 输出10 全局函数std::coutget()n;// 输出10 默认优先全局return0;}踩坑提醒匿名命名空间若存在与全局同名函数直接调用会触发编译歧义错误。九、统一初始化 {} C20 指派初始化器根治类型截断 bug9.1 统一初始化C11 核心特性在 C11 之前结构体、类、基础变量初始化语法割裂{}统一初始化语法抹平所有类型差异优势拉满全类型通用等号可省略书写统一{}空括号自动零初始化数值 0、字符串空、指针nullptr禁止窄化隐式转换double 转 int、long 转 short 直接编译报错杜绝隐蔽数值 bug支持 new 动态数组、类内数组成员、标准容器初始化对比演示#includeiostreamintmain(){// 多种初始化方式对比inta3;intb(3);intc{3};// 统一初始化推荐写法intd{};// 零初始化d0// 窄化转换直接报错规避隐患// int err{3.14}; 编译失败禁止double截断为intreturn0;}若业务确实需要窄化转换官方推荐 GSL 库gsl::narrow_cast显式转换语义清晰。9.2 C20 指派初始化器聚合类型专属针对结构体、数组这类聚合类型.成员名值显式赋值可读性大幅提升优势可跳过部分成员自动填充类内默认值或 0结构体新增成员时旧代码无需修改向下兼容一眼看懂每个字段赋值逻辑对比纯数字初始化列表可读性碾压#includeformat#includeiostreamstructEmployee{charfirstInitial;charlastInitial;intemployeeNumber;intsalary{75000};// 类内默认薪资};intmain(){// 传统统一初始化全靠顺序记忆字段Employee emp1{J,D,42,80000};// C20指派初始化显式绑定成员Employee emp2{.firstInitialJ,.lastInitialD,.employeeNumber42,.salary80000};// 跳过employeeNumber自动置0salary使用默认75000Employee emp3{.firstInitialL,.lastInitialM};std::coutstd::format(emp3默认薪资{}n,emp3.salary);return0;}限制赋值顺序必须和结构体成员声明顺序保持一致不能混用普通初始化与指派初始化。结尾总结从 C11 到 C20语言本身一直在弱化老式 C 风格写法用安全容器、标准化语法、编译期校验规避底层内存隐患。给大家整理一份开发优先级规范日常编码直接遵循容器选择std::vector(动态) /std::array(定长) 裸 C 数组初始化统一{}初始化 括号 / 等号赋值聚合类型优先 C20 指派初始化空值处理std::optional替代 - 1、nullptr 特殊标记复合数据拆解结构化绑定简化 pair / 结构体 / 数组取值面向对象私有数据 public 读写接口严格封装循环遍历范围 for 循环优先减少下标操作降低越界风险善用这套现代 C 体系既能保留底层性能又能大幅减少内存越界、类型截断、空值判断失误等经典 bug写出简洁、易维护、高健壮性工程代码。