ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

cppcheck 的 uselessCallsConstructor 检查:识别容器自我切片赋值的低效构造调用

cppcheck 的 uselessCallsConstructor 检查:识别容器自我切片赋值的低效构造调用 开发工具静态分析代码质量质量保障【免费下载链接】cppcheckstatic analysis of C/C code项目地址https://gitcode.com/gh_mirrors/cpp/cppcheck点击查看免费下载导读uselessCallsConstructor是 C 静态分析工具 cppcheck 在 STL 相关检查CheckStl中提供的一项性能检查当代码用一个从容器自身begin()迭代器范围构造出的临时容器如s { s.begin(), s.begin() end }再赋值回原容器时它会提示这是一次不必要的部分自我拷贝并建议改用erase()或resize()原地裁剪。本文围绕 man/checkers/uselessCallsConstructor.md 展开从告警含义、触发原理、修复方式、源码实现到测试用例完整讲解这一检查器的工作原理与实战用法。告警信息速览项目内容检查器 IDuselessCallsConstructor所属检查模块CheckStlSTL 容器相关检查消息文本Inefficient constructor call: container x is assigned a partial copy of itself. Use erase() or resize() instead.类别 / 严重级别Performance性能适用语言C关联 CWECWE-398不必要代码/低效代码的代码质量问题默认输出格式[文件:行:列]: (performance) 消息 [uselessCallsConstructor]该告警在 lib/checkstl.cpp 中通过reportError(tok, Severity::performance, uselessCallsConstructor, ..., CWE398, Certainty::normal)生成属于确定度Certainty为 normal 的常规报告而非 inconclusive 推断。告警触发条件容器被自身的部分切片赋值模式一花括号初始化列表 begin() 迭代器最典型的触发写法如下与文档中的示例一致#include string std::string f(std::string s, std::size_t end) { s { s.begin(), s.begin() end }; // - an unnecessary partial self-copy return s; }其语义是用s自己的两个迭代器[begin, beginend)构造一个临时std::string再把这个临时对象整体赋值回s。从结果上看这等价于把s原地裁剪到前end个字符但代价是先为切片内容分配一块全新的缓冲区并拷贝数据再通过赋值操作把拷贝结果搬回原容器临时对象析构时还要释放那块新缓冲区。数据明明已经待在正确的位置却被白白复制了一份。模式二显式类型构造 自我迭代器在 lib/checkstl.cpp 的检测逻辑中除花括号形式外还覆盖了显式构造临时对象的写法例如std::string f(std::string s, std::size_t end) { s std::string{ s.begin(), s.begin() end }; return s; } // 或函数式构造 std::vectorint f(std::vectorint v, std::size_t end) { v std::vectorint(v.begin(), v.begin() end); return v; }适用的容器类型从 lib/checkstl.cpp 的入口条件tok-valueType() tok-valueType()-type ValueType::CONTAINER可以看出检查器通过 valueType 判定目标是否为 STL 容器类型因此不仅限于std::string凡是以迭代器范围构造并支持erase()/resize()的容器如std::vector、std::list、std::deque等均在此检查范围内。如何修复改用 erase() 或 resize() 原地裁剪文档给出的推荐修复方式Before/After 对如下修复前#include string std::string f(std::string s, std::size_t end) { s { s.begin(), s.begin() end }; // - an unnecessary partial self-copy return s; }修复后#include string std::string f(std::string s, std::size_t end) { s.resize(end); return s; }两种修复思路的选择resize(end)语义是保留前end个元素其余丢弃。对std::string、std::vector、std::deque这类顺序容器尤其简洁直接是文档示例的首选方案erase(begin end, end())即s.erase(s.begin() end, s.end())显式删除从begin()end到末尾的区间。它适用于所有支持区间删除的容器包括std::list语义更贴近裁剪尾部一段的原始意图。两者都避免创建任何临时容器复杂度上不引入额外拷贝而原写法s { s.begin(), s.begin() end }至少要经历一次临时对象的构造、一次赋值拷贝和一次析构属于典型的为了表达同样的最终结果而多付一次拷贝成本。值得注意的是resize()只能保留前缀从容器开头到某个位置。如果裁剪的是中间或末尾的连续区间则应优先考虑erase()。而如果切片起点不是begin()例如想保留[mid, end)段检查器不会触发因为此时原地resize()无法等价表达。源码实现解析触发入口uselessCalls() 的统一分发uselessCallsConstructor并非独立函数而是由 CheckStl 的uselessCalls()统一分发的若干无用/低效 STL 调用检查之一。在 lib/checkstl.cpp 中该函数按顺序匹配多种模式其中与本节相关的分支是else if (printPerformance tok-valueType() tok-valueType()-type ValueType::CONTAINER) { if (Token::Match(tok, %var% { %var% . begin ( ) ,) tok-varId() tok-tokAt(3)-varId()) uselessCallsConstructorError(tok); else if (const Variable* var tok-variable()) { std::string pattern %var% ; for (const Token* t var-typeStartToken(); t ! var-typeEndToken()-next(); t t-next()) pattern t-str(), pattern ; pattern {|( %varid% . begin ( ) ,; if (Token::Match(tok, pattern.c_str(), tok-varId())) uselessCallsConstructorError(tok); } }从中可以提炼出三条关键实现事实左值与右值必须为同一变量tok-varId() tok-tokAt(3)-varId()或模式中的%varid%要求赋值目标与begin()的调用者变量 ID 完全一致因此a { b.begin(), ... }a、b是不同对象不会触发两种语法形态都被覆盖花括号初始化列表{ ... }与显式类型构造Type{ ... }/Type( ... )后者通过拼接变量自身的完整类型名生成动态匹配模式只要求 begin() 与逗号紧随匹配%var% . begin ( ) ,即可命中说明检测对象是从自身begin()起的迭代器范围构造而具体偏移表达式begin() end、begin() n - m等不参与匹配。此外整个uselessCalls()入口处有等级开关lib/checkstl.cpp只有Severity::performance或Severity::warning在启用集合中时才继续执行这也解释了为什么在默认启用 performance 等级时该检查会生效。报错函数uselessCallsConstructorErrorvoid CheckStlImpl::uselessCallsConstructorError(const Token *tok) { const std::string container tok ? tok-str() : ; const std::string msg Inefficient constructor call: container container is assigned a partial copy of itself. Use erase() or resize() instead.; reportError(tok, Severity::performance, uselessCallsConstructor, msg, CWE398, Certainty::normal); }告警文本中的容器名x直接取自触发位置 token 的字符串即赋值目标变量名消息中的建议Use erase() or resize() instead.与文档描述完全对应。检查注册与调度函数声明位于 lib/checkstl.hvoid uselessCalls();与 lib/checkstl.hvoid uselessCallsConstructorError(const Token* tok);实际调度在 lib/checkstl.cpp 的CheckStl::runChecks()中checkStl.uselessCalls()与erase()、if_find()、outOfBounds()等十余项检查并列执行runChecks开头if (!tokenizer.isCPP()) return;lib/checkstl.cpp说明整套 CheckStl含本检查仅对 C 代码生效C 代码不会进入该流程检查器元信息登记在 lib/checkers.cpp{CheckStl::uselessCalls,performance,warning}可供--checkers-report等命令导出。测试用例印证测试位于 test/teststl.cpp 的uselessCalls()用例组其中直接覆盖本告警的断言包括check(std::string f(std::string s, std::size_t end) {\n s { s.begin(), s.begin() end };\n return s;\n }\n); ASSERT_EQUALS([test.cpp:2:5]: (performance) Inefficient constructor call: container s is assigned a partial copy of itself. Use erase() or resize() instead. [uselessCallsConstructor]\n, errout_str());同组测试还验证了std::listint的花括号形式test/teststl.cppstd::string的std::string{ ... }与std::string(...)两种显式构造形式test/teststl.cppstd::vectorint的函数式构造形式test/teststl.cpp。这些断言同时确认了告警输出格式[文件:行:列]: (performance) ... [uselessCallsConstructor]以及左右两侧必须为同一容器的判定约束——测试中的s1.swap(s2)、s1.compare(s2)等不同对象调用分别命中uselessCallsSwap、uselessCallsCompare而非本检查。在 cppcheck 中的使用方式uselessCallsConstructor属于默认启用的 performance 等级告警无需额外命令行参数即可生效。实际使用时的几个要点运行静态分析时普通命令行即可触发例如cppcheck --enableperformance yourfile.cpp若只想看该项也可配合--enablewarning,performance--enableall同样包含它。告警 IDuselessCallsConstructor可用于抑制suppression与基线管理例如在代码中内联注释s { s.begin(), s.begin() end }; // cppcheck-suppress uselessCallsConstructor或通过命令行--suppressuselessCallsConstructor全局排除。在--template自定义输出模板、GUIgui/mainwindow.cpp与 CI 日志中均可通过该 ID 过滤、统计此项性能告警。与相邻检查的关系uselessCallsConstructor只是uselessCalls()分发的一组低效 STL 调用告警之一同族检查包括见 lib/checkstl.cpp检查 ID触发示例建议uselessCallsCompares2.compare(s2)与自身比较恒返回 0无意义uselessCallsSwaps2.swap(s2)与自身交换无效果uselessCallsSubstrs s.substr(0, n)等改用resize()/replace()/直接赋值uselessCallsEmpty独立语句v.empty();疑似应为clear()uselessCallsRemove忽略std::remove()返回值元素仍留在容器中uselessCallsSubstr与uselessCallsConstructor在语义上互补前者针对substr()前缀自我赋值后者针对迭代器范围构造的自我切片赋值两者给出的修复建议resize()/erase()也一脉相承。当代码同时存在s s.substr(0, s.size() - 1)与s { s.begin(), s.begin() n }两种原地截断写法时cppcheck 会分别以uselessCallsSubstr与uselessCallsConstructor提示改用更高效的原地操作。小结uselessCallsConstructor是 cppcheck 针对 STL 容器自我切片赋值这一常见低效写法的性能告警它利用 token 级模式匹配确认赋值左右两侧是同一容器、且右值是源自其自身begin()的迭代器范围构造从而在不改变程序语义的前提下建议开发者用resize()/erase()消除多余的临时拷贝。理解了 lib/checkstl.cpp 中的匹配逻辑与 test/teststl.cpp 的断言覆盖范围后即可在代码评审、CI 门禁和性能优化中准确运用这一检查。赞分享开发工具静态分析代码质量质量保障【免费下载链接】cppcheckstatic analysis of C/C code项目地址https://gitcode.com/gh_mirrors/cpp/cppcheck点击查看免费下载相关推荐cppcheck selfAssignment 检查项深度解析识别 x x 冗余自赋值cppcheck selfAssignment 检查项深度解析识别 x x 冗余自赋值 本文导读 本文围绕 cppcheck 官方检查项文档 man/c开发工具静态分析代码质量质量保障cppcheck 的 eraseIteratorOutOfBounds 检查器识别并修复容器越界 erase() 调用cppcheck 的 eraseIteratorOutOfBounds 检查器识别并修复容器越界 erase 调用 eraseIteratorOutOfBou开发工具静态分析代码质量质量保障cppcheck duplicateConditionalAssign 检查详解识别与 if 条件重复的冗余赋值cppcheck duplicateConditionalAssign 检查详解识别与 if 条件重复的冗余赋值 duplicateConditionalAs开发工具静态分析代码质量质量保障上一篇AgentOS 浏览器扩展实战指南在 Chrome 中为 GitHub、Jira 与 AWS 控制台运行受治理的 AI Agent下一篇GTA5线上小助手免费强大的游戏辅助工具终极指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表