尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

# 【C++ 面试真题】28. 聊聊 C++ 的裸指针改造

# 【C++ 面试真题】28. 聊聊 C++ 的裸指针改造 【C 面试真题】聊聊 C 的裸指针改造接手一个 C 老项目几千行代码里散落着裸 new/delete、malloc/free谁拥有谁说不清、哪条路径漏了释放没人知道——改一行崩三处。这是工程里最真实的内存战役也是内存管理篇的收官题。背得出换智能指针只是及格真考你的是改造的先后路线、和 C API 怎么交接、纯观察的裸指针该不该动、真实问题怎么一步步排查。本文按改造 排查 综合应用讲透。一、开场遗留代码的病灶❓ 一个满地裸指针的项目问题通常有哪几类✅ 先诊断、再动刀。四类典型病灶病灶典型代码危害所有权不明两个函数都 delete 同一指针double free路径遗漏异常/提前 return 漏 delete泄漏配对混用malloc 配 delete、new[] 配 deleteUBUndefined Behavior崩溃诡异接口契约靠猜返回的指针谁释放没人知道泄漏或 UAFUse-After-Free回答思路先说诊断重于动手——搞清每块内存的 owner 是谁再分类改造上来无脑替换智能指针多线程/共享场景反而会引入循环引用新坑。二、改造路线四步走风险递增❓ 改造顺序怎么定✅ 按风险从低到高四步走每步可独立验证第一步局部 new/delete → unique_ptr最安全立刻消灭泄漏// 改造前voidf(){char*bufnewchar[1024];if(!ok){delete[]buf;return;}process(buf);delete[]buf;}// 改造后三条路径的 delete 全消失voidf(){autobufmake_uniquechar[](1024);if(!ok)return;process(buf.get());}第二步工厂/接口返回值 → 返回 unique_ptr把所有权写进类型// 旧裸指针返回调用方猜要不要删Conn*createConn();// 新签名就说清所有权交给你unique_ptrConncreateConn();第三步所有权确实共享的 → shared_ptr慎用先确认真共享// 多个组件都要持有、生命周期不定shared_ptrCachecache_;// 谨记能 unique 不 shared// 互相引用处配 weak_ptr第四步C API 交接 → 智能指针 自定义删除器下节专讲。顺序即策略先 1 后 2 再 3——大部分代码走到第 1、2 步就治好了第 3 步是确认共享而不是图省事。shared_ptr 滥用是改造中新生的最大坏味道。三、和 C API 交接删除器的妙用❓ 和 C 接口malloc 的、fopen 的、第三方库的怎么交接✅用自定义删除器把怎么释放打包进智能指针——资源的关闭姿势一次性说清// FILE* 的正确归宿fcloseautofileunique_ptrFILE,decltype(fclose)(fopen(a.txt,r),fclose);// 离开作用域自动 fclose// malloc 的内存free 来收autobufunique_ptrvoid,decltype(free)(malloc(4096),free);// 第三方库库自己的释放函数autohunique_ptrHandle,LibDeleter(LibCreate(),LibDeleter{}); 这招的精髓资源管理权和资源语义解耦——unique_ptr 负责必然释放删除器负责按规矩释放。无论多古董的 C 接口都能纳入 RAII 体系。⚠️ 两个细节unique_ptrvoid, D合法void 也行只要删除器懂它fclose指针在 MSVC 上因[[nodiscard]]标注可能需要包一层 lambda——动手时留意编译器提示。四、不该动的观察指针与 C 兼容层❓ 所有裸指针都要消灭吗✅ 不。两种裸指针是合法且更好的存在① 纯观察指针——只是看看不参与生命周期// 参数只是借用传裸指针/引用voidprint(conststring*s);// 不需要 shared_ptr 接收——// 所有权语义和开销都是误导② 与 C 交互的边界——get()把管辖区内的资源临时递给老接口autobufmake_uniquechar[](n);legacy_fill(buf.get(),n);// C 函数填完就完指针还在// unique_ptr 手里安全 判断标准一句话“谁负责释放如果是这个指针必须智能指针化如果只是指过去看看”裸指针/引用就是正确工具。消灭的是所有权裸指针不是裸指针本身。五、综合应用一次完整的问题排查❓ 线上服务内存持续上涨怎么一步步排查✅ 按流程走一遍面试讲出这个流程就是高级感① 先定性泄漏还是正常波动监控RSS 曲线只涨不跌 压测后回落 → 缓存/空闲池非泄漏 单调上涨 → 疑似泄漏进入 ②② 复现与插桩测试环境同负载跑开-fsanitizeaddress——退出时 LeakSanitizer 直接给出每块泄漏的分配调用栈。③ 读栈定位病灶Direct leak of 1024 byte(s) #1 Conn::Conn() conn.cpp:42 #2 Pool::create() pool.cpp:77顺着栈看代码pool.cpp:77里 new 出的 Conn 注册进了列表析构却没人清——病灶是注册了没注销不是简单的忘了 delete。④ 按改造路线修注册列表本来就用裸指针存这是合法观察但创建处该返回unique_ptr进列表时shared_ptr/weak_ptr按真实所有权定。⑤ 回归验证ASan 跑干净 监控曲线平稳结案。 注意 ③ 的教训工具报告的是分配点病灶常在所有权设计——修一行 delete 是止痛理顺所有权才是治病。六、改造纪律别引入新问题❓ 改造最容易引入哪些新坑✅ 四个高频新坑动手前心里有数shared_ptr 循环引用——图省事全换 shared父子互指成环泄漏换了马甲回来。互指处必须 weak_ptr所有权双轨——同一资源一会儿裸指针一会儿智能指针边界处 double free。入口唯一资源诞生地立即进智能指针此后只传引用/get()生命周期延长过度——本来作用域即释放的资源被 shared_ptr 拖到全局内存峰值上升线程边界的悬空——回调里持有的裸指针对象早被析构。异步持有一律 shared_ptr/weak_ptr lock。 一条总纪律每块内存自诞生起owner 唯一且写在类型上。改造完成的标志不是没有裸指针而是每行代码都能回答这块内存现在归谁。七、面试高频追问❓ Q1接手百万行裸指针代码从哪里开始✅ 不是大面积替换三步先挂clang-tidy拿全量报告做泄漏地图再挑热点路径崩溃/泄漏集中的模块按四步路线改造、ASan 验证同时立规矩——新代码必须 RAII老代码touch 到就改。技术债靠增量消化一次性重写是最大风险。❓ Q2unique_ptr.get() 交出去的指针对方存起来用了怎么办✅ 这就是引入 UAF 的典型操作。纪律是get()只用于同步调用、当场用完的场景对方若需要持有要么传shared_ptr共享要么传weak_ptr观测 lock不能存裸指针过夜。❓ Q3资源不是内存文件/socket/锁也能这么拯救吗✅ 能这正是 RAII 的本意——unique_ptrFILE, decltype(fclose)管 FILElock_guard管锁自定义删除器管任意打开/关闭型资源。RAII 拯救的不只是内存是一切成对出现的资源操作。❓ Q4为什么说修一行 delete 是止痛✅ 工具给的分配栈只是症状发生地病因是所有权设计谁注册、谁注销、谁共享。头痛医头的补丁会漏掉同类路径——同构的 bug 在下一个函数里等着。改造必须落到owner 唯一的结构上。❓ Q5改造后如何证明没变慢✅ 智能指针的额外开销可量化unique_ptr 零开销shared_ptr 每次 copy/reset 是原子计数纳秒级。基准测试对比改造前后热点路径确认无循环引用、无过度生命周期延长性能差距通常在噪声内——结构正确的代码才有资格谈性能。❓ Q6C 项目里混着 malloc 的部分第三方库怎么管✅ 边界收口库的 API 用删除器包装第三节的 unique_ptr free/closer包装层以内是 RAII 世界、以外是库的地盘——门面上永远只出现智能指针和引用。malloc 家族本身不消灭只是被圈养在边界上。八、总结速查表考点一句话结论第一步局部 new/delete → unique_ptr第二步工厂返回 unique_ptr所有权入签名第三步真共享才 shared互指配 weakC API 交接自定义删除器打包释放语义合法裸指针纯观察、get() 当场用排查流程定性 → ASan → 读栈 → 修所有权 → 回归新坑循环引用、双轨所有权、悬空回调总纪律每块内存 owner 唯一且写在类型上一句话回顾拯救散落的 free/delete先诊断所有权、按unique → 接口 → shared → C 交接四步走C API 用删除器圈进 RAII 边界纯观察的裸指针不动排查靠定性 → ASan → 读栈 → 修所有权而完成的标志是——每块内存都能回答现在归谁。内存管理篇到此收官下期进入多线程篇敬请关注
返回列表