ARTICLE DETAIL

资讯详情

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

cppcheck uselessOverride 检查器详解:识别冗余虚函数重写,消除无意义的间接调用

cppcheck uselessOverride 检查器详解:识别冗余虚函数重写,消除无意义的间接调用 开发工具静态分析代码质量质量保障【免费下载链接】cppcheckstatic analysis of C/C code项目地址https://gitcode.com/gh_mirrors/cpp/cppcheck点击查看免费下载导读uselessOverride是 C 静态分析工具 cppcheck 内置的一项代码质量Code Quality检查用于识别派生类中多此一举的虚函数重写要么函数体与基类版本逐字相同要么只是简单调用基类版本并原样返回结果。本文将以 man/checkers/uselessOverride.md 为骨架结合lib/checkclass.cpp的实现与 test/testclass.cpp 的测试用例完整讲解该检查的触发条件、误报防护bailout逻辑、修复方式以及与missingOverride检查的分工。读完本文你将能准确判断哪些虚函数重写属于无用代码并理解 cppcheck 如何在符号表与 Token 级别对比两段函数体。检查器速览uselessOverride的元信息定义在文档头部可在运行结果中直接对应消息The function f is an unnecessary overload; it is identical to the parent function类别Code Quality代码质量严重级别Style风格适用语言C实际运行中cppcheck 会输出两种更精确的提示见 uselessOverrideError函数体与基类完全相同时The function f overrides a function in a base class but is identical to the overridden function仅委托回基类时The function f overrides a function in a base class but just delegates back to the base class.两者的错误 ID 均为uselessOverrideCWE 编号未知CWE(0)确定性级别为 normal。输出采用多行定位格式例如测试中验证的[test.cpp:1:24] - [test.cpp:3:9]: (style) The function f overrides a function in a base class but just delegates back to the base class. [uselessOverride]其中[基类位置] - [派生类位置]构成一条 error path第一处指向基类虚函数定义第二处指向派生类重写函数见 errorPath 构造逻辑 lib/checkclass.cpp#L3368-L3372。检查内容两类无用重写按照 文档描述一个重写函数在以下两种情况下会被判为无用函数体与基类版本在语义上完全一致——派生类把基类代码逐字抄了一遍函数体只调用基类版本并返回其结果——即所谓的委托回基类delegation除了一次额外的函数调用间接层外没有任何行为差异。两者都意味着这个 override 不改变程序今天的行为属于纯粹的死重dead weight。文档的 Motivation 进一步说明其危害它增加了一层间接调用并制造了两份可能在未来意外漂移accidentally drift apart的代码副本——一旦基类演进而派生类忘记同步两处实现就会悄悄分道扬镳。触发示例委托回基类文档给出的Before示例struct B { virtual int f() { return 5; } }; struct D : B { int f() override { return B::f(); } // - uselessOverride: identical to the base version };D::f的唯一动作就是把调用转发给B::f并返回任何通过D的指针调用f()的代码最终执行的都是基类实现重写本身毫无意义。触发示例逐字复制基类实现测试 testclass.cpp#L9063-L9077 验证了函数体相同这一分支。其中B::g()返回7而D::g()也返回7逐 token 对比完全一致struct B { virtual int g() { return 7; } virtual int j(int i, int j) { return i j; } }; struct D : B { int g() override { return 7; } // 触发is identical to the overridden function int j(int i, int j) override { return i j; } // 触发is identical to the overridden function };而同一测试中h(int j, int i) override { return i j; }不会触发——虽然计算结果相同但参数名与基类不一致Token 对比阶段即判定为不同代码。同理B::f(i 1)对参数做变换与B::f(j, i)交换参数顺序也都不触发testclass.cpp#L8998-L9008。修复方式删除多余重写文档给出的After示例非常直接——去掉这个不产生任何行为差异的 override 即可struct B { virtual int f() { return 5; } }; struct D : B { };删除后D自然继承B::f的虚函数表条目与实现虚调用仍会正确派发到基类函数程序行为完全不变同时消除了间接层与潜在的代码漂移点。实现原理从符号表到 Token 对比该检查由 CheckClassImpl::checkUselessOverride 实现在runChecks中被调用lib/checkclass.cpp#L3927并注册为 style 级检查lib/checkers.cpp#L62。整体流程可分五步前置过滤若 style 严重级别未启用且非 premium 模式直接返回L3429-L3430。遍历类只处理单一直接基类derivedFrom.size() 1的 struct/class跳过多继承场景L3434-L3436。候选函数筛选对类中每个有函数体的函数跳过带final说明符的、无被重写函数getOverriddenFunction()为空的、基类函数为纯虚的、以及访问级别与基类不一致的若存在同名重载也跳过L3437-L3450。分支一——逐字相同当基类函数也有函数体时先用 compareTokenRanges 对比参数列表再用同样的方式对比整个函数体从bodyStart到bodyEnd。对比逐 Token 进行任何str不同即判定不同同时约定遇到this、宏展开 TokenisExpandedMacro()立即中止并视为不同保守处理。只有两段都完全相同才报identical to the overridden functionL3453-L3474。分支二——单次委托调用getSingleFunctionCall 解析函数体要求恰好是一条语句return B::f(...)或B::f(...);且被调函数就是基类被重写函数本身调用参数必须与形参列表一一对应同名且不允许经.成员访问如b.f()。满足才报just delegates back to the base classL3475-L3487。分支一之前的额外防护在确认逐字相同后代码还会调用getDuplInheritedMembersRecursive/getDuplInheritedMemberFunctionsRecursive检查是否存在被遮蔽的成员或成员函数shadowed members若存在则放弃报告L3460-L3469避免误伤需要显式重写以破除遮蔽的合法场景。边界情况与误报防护bailout测试用例 uselessOverride 覆盖了大量不应报告的边界情况可作为理解检查器保守性的权威清单场景不报告的原因测试位置纯虚函数 0带类外定义派生类委托调用基类纯虚函数没有可作为相同实现的函数体委托到类外定义版本属于常见合法用法L8985-L8990参数经过变换B::f(i 1)或交换B::f(j, i)非逐字委托函数体 Token 或实参映射不一致L8998-L9008多重继承struct D : B, I检查要求单一直接基类L9010-L9015派生类使用final显式终止虚派发可能是有意设计L9017-L9021基类 protected、派生类 public访问级别变化baseFunc-access ! func.accessL9023-L9031委托调用中混入成员变量B::f(a, b, m)实参与形参不一致L9033-L9041类内存在同名重载函数重载存在时判定可能失真跳过L9043-L9052三层继承链中的中间空实现空函数体不构成委托或相同L9054-L9061throw *this形式的相同实现继承std::exception涉及异常对象切片等复杂语义保守跳过L9079-L9085经宏展开定义的函数Token 标记为宏展开即中止对比L9087-L9094、L9119-L9124派生类拥有同名成员变量m遮蔽基类相同文本在不同类中语义不同L9096-L9107内部调用被派生类重写f(){g();}且D::g为 virtual两处g()调用目标不同行为不等价L9126-L9134委托到成员对象b.f()带.属于成员访问而非对基类版本的委托L9136-L9144从这些用例可以推断checkUselessOverride采用保守策略——凡是对比无法完全确认等价、或语义可能受遮蔽、宏、访问级别、异常与多态影响的情形一律不报告宁可漏报也不误报。关联检查器与 missingOverride 的分工文档将 missingOverride.md 列为关联检查器。两者的关注点正交missingOverride派生类函数与基类虚函数签名相同但没有写override说明符——不管这个重写是否无用。其动机是防止将来基类签名改动时派生函数悄悄退化为无关新函数而不被察觉。uselessOverride函数确实正确标注并重写了基类虚函数但重写不改变任何行为。两者互补前者强迫你显式声明意图后者督促你删掉没有意图的声明。二者的错误输出也共享相同的 error path 格式与类似的报告函数结构对比 missingOverrideError 与 uselessOverrideError。如何运行uselessOverride属于 style 级别的默认检查运行 cppcheck 即可触发。针对单个文件cppcheck --enablestyle your_file.cpp若只想查看该检查器的输出可配合--checkers-report或直接过滤错误 IDcppcheck --enablestyle your_file.cpp 21 | grep uselessOverride注意从 checkUselessOverride 的门槛判断若项目通过--suppressstyle等方式关闭了 style 级别该检查将被跳过。由于检查依赖类层次信息建议让 cppcheck 通过编译数据库--projectcompile_commands.json或--inline-suppr配置获得完整符号上下文以获得更准确的基类判定。小结uselessOverride是 cppcheck 面向 C 虚函数体系的一项低成本、低风险清理工具它在符号表SymbolDatabase基础上对函数体做逐 Token 对比识别逐字相同与委托回基类两种冗余重写同时通过单继承限制、宏/final/访问级别/遮蔽成员等十余项 bailout 规则保持极高的误报防护水准。配合missingOverride一起使用可以帮助团队在虚函数该不该写与写了有没有用两个维度上都保持代码整洁。相关的实现细节可继续阅读 lib/checkclass.cpp 与 test/testclass.cpp 中的对应测试。赞分享开发工具静态分析代码质量质量保障【免费下载链接】cppcheckstatic analysis of C/C code项目地址https://gitcode.com/gh_mirrors/cpp/cppcheck点击查看免费下载相关推荐cppcheck redundantPointerOp 检查器详解识别并消除 *p 与 *p 冗余指针运算cppcheck redundantPointerOp 检查器详解识别并消除 p 与 p 冗余指针运算 导读 redundantPointerOp 是开发工具静态分析代码质量质量保障cppcheck 内部检查器 redundantNextPrevious消除 Token 链式调用中的冗余往返cppcheck 内部检查器 redundantNextPrevious消除 Token 链式调用中的冗余往返 导读 redundantNextPreviou开发工具静态分析代码质量质量保障cppcheck duplicateConditionalAssign 检查详解识别与 if 条件重复的冗余赋值cppcheck duplicateConditionalAssign 检查详解识别与 if 条件重复的冗余赋值 duplicateConditionalAs开发工具静态分析代码质量质量保障上一篇如何快速掌握text2vec文本向量化的终极入门指南下一篇探索PYSearch强大的Python全文搜索库创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表