本系列是开源书 C ++ Best Practises 的中文版,全书从工具、代码格调、安全性、可维护性、可移植性、多线程、性能、正确性等角度全面介绍了古代 C ++ 我的项目的最佳实际。本文是该系列的第一篇。
前言
C++ 最佳实际: 反对 Fork 的编码标准文档
本文档旨在收集对 C ++ 最佳实际所进行的协作性探讨,是《Effective C++》(Meyers) 和《C++ Coding Standards》(Alexandrescu, Sutter) 等书籍的补充。在探讨如何确保整体代码品质的同时,补充了一些没有探讨到的较低级别的细节,并提供了具体的格调倡议。
在任何状况下,简单明了都是首选。本文所举示例是为了阐明为什么一种抉择比另一种更受欢迎。在必要状况下,也会用文字说明。
本文档由 Jason Turner 编写,依据常识共享署名 - 非商业 4.0 国内许可协定受权。
免责申明
本文档的编写基于集体教训,你不须要完全同意其中的观点。本文档保留于 GitHub 上,任何人都能够 fork 供本人应用,或者提交批改倡议与大家分享。
本文档启发 O ’Reilly 公布了视频: Learning C++ Best Practices
工具
应该在开发过程的晚期建设用于执行这些工具的自动化框架,检出源代码、构建和执行测试所应用的命令不应超过 2 - 3 个,一旦测试实现,应该对代码的状态和品质有靠近残缺的理解。
源码治理
对于任何软件开发我的项目来说,源码治理都是相对必要的,如果还没有,那就开始应用。
- GitHub —— 容许无限度的公共存储库和公有存储库,反对最多 3 个协作者。
- Bitbucket —— 容许无限度的私人存储库,最多 5 个协作者,收费。
- SourceForge —— 仅反对托管开放源码。
- GitLab —— 收费提供有限的公共和公有存储库,包含有限的 CI 执行器(CI Runner)。
- Visual Studio Online (http://www.visualstudio.com/what-is-visual-studio-online-vs) —— 有限的公共存储库,公有存储库免费,反对 git 或 TFVC。另外提供: 问题跟踪、我的项目打算(包含 Scrum 等多个麻利模板)、集成托管构建,所有个性都能够集成到 Microsoft Visual Studio 中,仅反对 Windows。
构建工具
应用宽泛承受的行业标准构建工具,能够避免在做摸索、链接新库、打包产品等等工作时反复创造轮子。例子包含:
-
CMake
- 对于构建性能,请思考: https://github.com/sakra/cotire
- 对于加强可用性,请思考: https://github.com/toeb/cmakepp
- 应用 https://cmake.org/cmake/help/v3.6/command/target_compile_feat… 作为 C ++ 规范 flag
- 思考应用 https://github.com/cheshirekow/cmake_format 主动格式化 CMakeLists.txt 文件
- CMake 特定最佳实际请参考后续的延长浏览局部
cmake --build
提供了平台无关的通用编译接口
- Waf
- FASTBuild
- Ninja —— 能够极大优化大型项目的增量构建工夫,能够作为 CMake 的 target。
- Bazel —— 基于网络工件缓存和近程执行的疾速增量构建
- Buck —— 相似于 Bazel,对 iOS 和 Android 有很好的反对
- gyp —— 谷歌 chromium 的构建工具
- maiken —— 具备 maven 配置格调的跨平台构建工具
- Qt Build Suite —— 基于 Qt 的跨平台构建工具
- meson —— 疾速、对用户敌对的开源构建零碎
- premake
请记住,这不仅是构建工具,也是编程语言。请尽量保护良好整洁的构建脚本,并遵循正在应用的工具的举荐实际。
包管理器
包治理是 C ++ 的重要主题,目前还没有明确的赢家。请思考应用包管理器来帮忙跟踪我的项目的依赖关系,从而帮忙新人更容易开始参加我的项目。
- Conan —— 跨平台 C ++ 依赖管理器
- hunter —— CMake 驱动的跨平台包管理器,实用于 C /C++
- C++ Archive Network (CPPAN)”)”) —— 跨平台 C ++ 依赖管理器
- qpm —— Qt 的包管理器
- build2 —— 类 Cargo 的 C ++ 包管理器
- Buckaroo —— 真正去中心化的跨平台依赖管理器,实用于 C /C++ 等等
- Vcpkg —— 微软 C ++ 库管理器,反对 Windows, Linux 和 MacOS
继续集成
抉择了构建工具之后,接下来须要设置继续集成环境。
在更改被推送到存储库时会触发继续集成 (CI) 工具主动构建源代码,能够公有部署 CI 工具或应用托管的 CI 零碎。
-
Travis CI
- 能很好的与 C ++ 一起工作
- 设计与 GitHub 一起应用
- GitHub 公共存储库能够收费应用
-
AppVeyor
- 反对 Windows、MSVC 和 MinGW
- GitHub 公共存储库能够收费应用
-
Hudson CI / Jenkins CI
- 须要 Java 应用服务器
- 反对 Windows、OS X 和 Linux
- 能够通过许多插件进行扩大
-
TeamCity
- 对开源我的项目收费
-
Decent CI
- 简略继续集成,能够将后果公布到 GitHub
- 反对 Windows、OS X 和 Linux
- 应用 ChaiScript
-
Visual Studio Online (http://www.visualstudio.com/what-is-visual-studio-online-vs)
- 与 Visual Studio Online 的源代码库严密集成
- 应用 MSBuild (Visual Studio 的构建引擎),可在 Windows、OS X 和 Linux 上应用
- 提供托管的构建代理,也容许用户提供构建代理
- 能够在 Microsoft Visual Studio 中管制和监控
- 通过 Microsoft Team Foundation Server 进行外部装置
-
GitLab
- 应用自定义 Docker 镜像,因而可用于 C ++
- 有收费的共享执行器
- 提供简略的覆盖率后果剖析
如果在 GitHub 上有开源、公开托管的我的项目:
- 当初就把 Travis Ci 和 AppVeyor 整合起来。对于如何在基于 C ++ cmake 的应用程序中启用的简略示例,请参考: https://github.com/ChaiScript/ChaiScript/blob/master/.travis.yml
- 启用笼罩工具(Codecov 或 Coveralls)
- 启用 Coverity Scan
这些工具都是收费的,设置起来也绝对容易。一旦把它们都设置好,就能够对我的项目进行继续的构建、测试、剖析和报告,并且收费。
编译器
启用所有可用、正当的告警选项,有些告警选项只在启用了优化的状况下才无效,或者优化级别越高,成果越好,例如 GCC 中的-Wnull-dereference
。
应该应用尽可能多的编译器,每个编译器对规范的实现略有不同,反对多个编译器将有助于确保实现最可移植、最牢靠的代码。
GCC / Clang
-Wall -Wextra -Wshadow -Wnon-virtual-dtor -pedantic
-Wall -Wextra
正当、规范-Wshadow
如果变量申明笼罩了父上下文中的变量,则正告用户-Wnon-virtual-dtor
如果带有虚函数的类有非虚析构函数,则正告用户,有助于捕捉难以跟踪的内存谬误-Wold-style-cast
对 C 格调的类型转换收回正告-Wcast-align
正告有潜在性能问题的强制类型转换-Wunused
正告任何未应用的货色-Woverloaded-virtual
如果重载 (而不是笼罩) 虚函数,则收回正告-Wpedantic
如果应用了非标准的 C ++ 则收回正告(所有版本的 GCC, Clang >= 3.2)-Wconversion
对可能失落数据的类型转换收回正告-Wsign-conversion
对影响到符号的类型转换收回正告(Clang 所有版本,GCC >= 4.3)-Wmisleading-indentation
如果代码中有缩进,但没有对应的代码块,则收回正告(仅在 GCC >= 6.0 中)-Wduplicated-cond
如果if
/else
分支有反复条件,则收回正告(仅在 GCC >= 6.0 中)-Wduplicated-branches
如果if
/else
分支有反复的代码,则收回正告(仅在 GCC >= 7.0 中)-Wlogical-op
在可能须要按位操作的中央应用逻辑操作时收回正告(仅在 GCC 中)-Wnull-dereference
如果检测到空解援用将收回正告(仅在 GCC >= 6.0 中)-Wuseless-cast
如果执行强制转换到雷同的类型,则会收回正告(仅在 GCC >= 4.8 中)-Wdouble-promotion
如果float
隐式晋升为double
则收回正告(GCC >= 4.6, Clang >= 3.8)-Wformat=2
对输入格式化函数 (即printf
) 的平安问题收回正告-Wlifetime
显示对象生命周期问题(目前只有 Clang 的非凡分支)
思考应用-Weverything
,并且只在须要的状况下禁用多数正告。
-Weffc++
正告模式可能太吵了,但如果对我的项目实用,也能够应用。
MSVC
/permissive-
—— 执行规范一致性
/W4 /w14640
—— 应用并思考以下内容(参见上面的形容)
/W4
所有正当的正告/w14242
‘identifier’: 从 ’type1’ 到 ’type1’ 的转换,可能失落数据/w14254
‘operator’: 从“type1:field_bits”到“type2:field_bits”的转换,可能失落数据/w14263
‘function’: 成员函数不重写任何基类虚成员函数/w14265
‘classname’: 类有虚函数,但析构函数不是该类的虚实例,可能无奈正确析构/w14287
‘operator’: 无符号 / 负常数不匹配/we4289
nonstandard extension used: ‘variable’: 在 for 循环中申明的循环控制变量在 for 循环作用域之外应用/w14296
‘operator’: 表达式总是 ’ 布尔值(boolean_value)’/w14311
‘variable’: 指针从 ’type1’ 转换到 ’type2’ 时被截断/w14545
逗号前的表达式计算的是短少参数列表的函数/w14546
逗号前的函数调用短少参数列表/w14547
‘operator’: 逗号前的运算符有效,预期运算符有副作用/w14549
‘operator’: 逗号前的运算符有效,想要“运算符”吗?/w14555
表达式没有成果,表达式预期带有副作用/w14619
pragma warning: 没有正告号码/w14640
在线程不平安的动态成员初始化时启用正告/w14826
从 ’type1’ 到 ’type_2’ 的转换会扩大符号,可能会导致意外的运行时行为/w14905
宽字符串字面量转换为 ’LPSTR’/w14906
字符串字面量转换为 ’LPWSTR’/w14928
非法的拷贝初始化,已隐式利用多个用户定义转换
不倡议
/Wall
会对规范库中蕴含的文件收回正告,有太多额定的正告,因而没什么用。
通用
一开始就设置十分严格的正告,在我的项目开始后试图进步正告级别可能会很苦楚。
思考应用 将正告视为谬误的设置,例如 MSVC 中的/Wx
,以及 GCC/Clang 中的-Werror
。
基于 LLVM 的工具
基于 LLVM 的工具与可能输入编译命令数据库的构建零碎 (例如 cmake) 配合得最好,例如:
$ cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON .
如果没用这样的构建零碎,能够思考 Build EAR,它能够与现有构建零碎挂钩,并生成编译命令数据库。
CMake 当初也提供了在失常编译期间调用 clang-tidy
的内置反对。
- include-what-you-use, 示例后果
- clang-modernize, 示例后果
- clang-check
- clang-tidy
动态查看
最好的抉择是将动态分析器作为自动化构建零碎的一部分运行,cppcheck 和 clang 能够满足收费选项的要求。
Coverity Scan
Coverity 提供收费 (开源) 动态剖析工具包,能够用于与 Travis CI 和 AppVeyor 集成的每个提交。
PVS-Studio
PVS-Studio 是用于检测用 C、C++ 和 C# 编写的程序源代码中的 bug 的工具,对集体学术我的项目、开源非商业我的项目和集体开发者的独立我的项目都是收费的,能够在 Windows 和 Linux 环境下工作。
Cppcheck
Cppcheck 是收费、开源的。它努力争取零误报,并且做得很好。因而,应该启用所有正告: --enable=all
。
备注:
- 为了正确工作,须要格局残缺的头文件门路,所以在应用前不要遗记传递:
--check-config
。 - 查找未应用的头文件时
-j
不能大于 1。 - 如果须要查看所有的代码,请记住为带有大量 #ifdef 的代码增加
--force
。
cppclean
cppclean 是开源动态分析器,专一于发现 C ++ 源代码中导致大型代码库开发迟缓的问题。
CppDepend
CppDepend 通过剖析和可视化代码依赖关系、定义设计规定、进行影响剖析以及比拟不同版本的代码,简化了对简单 C /C++ 代码库的治理,对开源贡献者是收费的。
Clang 的动态分析器
Clang 的分析程序的默认选项实用于各个平台,能够间接通过 CMake 应用,也能够通过基于 llvm 的工具中的 clang-check
和clang-tidy
调用。
此外,CodeChecker 能够作为 clang 的动态剖析前端。
clang-tidy
能够通过 Clang Power Tools 扩大轻松的和 Visual Studio 一起应用。
MSVC 的动态分析器
能够通过 /analyze
命令行选项启用,能够应用默认选项。
Flint / Flint++
Flint 和 Flint++ 是依据 Facebook 编码标准剖析 C ++ 代码的 linter。
OCLint
OCLint 是收费、自在、开源的动态代码剖析工具,能够通过许多不同的形式进步 C ++ 代码的品质。
ReSharper C++ / CLion
这两种来自 JetBrains 的工具都提供了肯定水平的动态剖析和主动修复性能,为开源我的项目负责人提供了收费许可证选项。
Cevelop
基于 Eclipse 的 Cevelop IDE 提供了各种动态剖析和重构 / 代码修复工具。例如,能够用 C ++ 的 constexprs
替换宏,重构命名空间(提取 / 内联using
,限定名称),并将代码重构为 C ++11 的对立初始化语法。Cevelop 是收费的。
Qt Creator
Qt Creator 能够插入 clang 动态分析器。
clazy
clazy 是基于 clang 的剖析 Qt 应用状况的工具。
IKOS
IKOS 是开源动态分析器,由 NASA 开发。它以形象解释为根底,用 C ++ 编写,应用 LLVM 为 C 和 C ++ 提供了分析器。源代码能够在 Github 上找到。
运行时查看
代码覆盖率剖析
覆盖率剖析工具应该在测试执行时运行,以确保整个应用程序都被测到。可怜的是,覆盖率剖析须要禁用编译器优化,这将导致测试执行工夫大大缩短。
-
Codecov
- 与 Travis CI 和 AppVeyor 集成
- 对于开源我的项目收费
-
Coveralls
- 与 Travis CI 和 AppVeyor 集成
- 对于开源我的项目收费
-
LCOV
- 有很多配置项
- Gcovr
-
kcov
- 可与 codecov 和 coveralls 集成
- 不须要非凡的编译器 flag,只须要 debug 符号,就能够输入代码覆盖率报告
-
OpenCppCoverage
- Windows 上的开源代码覆盖率工具
Valgrind
Valgrind 是运行时代码分析器,能够检测内存透露、竞争条件和其余相干问题,反对各种 Unix 平台。
Dr Memory
和 Valgrind 相似。http://www.drmemory.org
GCC / Clang Sanitizers
这些工具提供了许多与 Valgrind 雷同的个性,但内置在编译器中,易于应用,并提供问题报告。
- AddressSanitizer
- MemorySanitizer
- ThreadSanitizer
- UndefinedBehaviorSanitizer
留神可用的 sanitizer 选项,包含运行时选项。https://kristerw.blogspot.com/2018/06/useful-gcc-address-sani…
Fuzzy 分析器
如果我的项目承受用户定义的输出,能够思考运行含糊输出测试。
这些工具都应用覆盖率报告来寻找新的代码执行门路,并尝试为代码提供新的输出。它们能够发现解体、挂起以及一些没有被思考到的输出。
- american fuzzy lop
- LibFuzzer
- KLEE —— 能够为独自的函数提供含糊测试
变异测试
这些工具获取在单元测试运行期间执行的代码,并扭转执行的代码。如果测试在有渐变的状况下依然通过,那可能意味着在测试套件中存在有缺点的测试。
- Dextool Mutate
- MuCPP
- mull
- CCMutator
控制流爱护
MSVC 的控制流爱护 (Control Flow Guard)”)”) 减少了高性能的运行时安全检查。
查看 STL 实现
_GLIBCXX_DEBUG
与 GCC 的 libstdc++ 的实现。参见 Krister 的博客文章。
堆剖析
- https://epfl-vlsc.github.io/memoro —— 一个具体的堆分析器
疏忽正告
如果团队统一认为编译器或分析器对不正确或不可避免的谬误收回正告,则团队须要尽可能只在最小的范畴内禁用特定的谬误正告。
在对一段代码禁用该正告后,请确保从新启用该正告,没人心愿禁用的正告被泄露到其余代码中。
测试
下面提到的 CMake 有一个用于执行测试的内置框架,请确保应用的任何构建零碎都可能执行内置测试。
为了进一步帮忙执行测试,请思考应用某个单元测试库,如 Google Test、Catch、CppUTest 或 Boost.Test,以帮忙组织测试。
单元测试
单元测试针对的是能够独立测试的小代码块和独立性能。
集成测试
对于提交的每个个性或 bug 修复,都应该启用测试。参见上文介绍的代码覆盖率剖析。这些测试比单元测试级别更高,但依然应该被限度在单个个性的范畴内。
逆向测试
不要遗记确保测试代码中的错误处理,并且确保其可能失常工作。如果指标是 100% 的代码覆盖率,很显著这些谬误场景也须要被笼罩的。
调试
uftrace
uftrace 能够用来生成程序执行的函数调用图。
rr
rr 是一个收费、开源的反向调试器,反对 C ++。
其余工具
Lizard
Lizard 提供了针对 C ++ 代码库运行复杂性剖析的非常简单的接口。
Metrix++
Metrix++ 能够辨认并报告代码中最简单的局部,从而帮忙咱们缩小简单代码,帮忙编译器更好的了解和优化代码。
ABI Compliance Checker
ABI Compliance Checker (ACC)能够剖析两个库版本,并生成对于 API 和 C ++ ABI 变动的具体兼容性报告,能够帮忙库开发人员发现无心的破坏性更改,以确保向后兼容性。
CNCC
Customizable Naming Convention Checker(可自定义的命名约定查看器)能够报告代码中不遵循特定命名约定的标识符。
ClangFormat
ClangFormat 能够主动查看并纠正代码格局,以匹配组织约定。能够参考对于 clang-format 的系列文章。
SourceMeter
SourceMeter 提供了收费版本,能够为代码提供许多不同的度量,也能够调用 cppcheck。
Bloaty McBloatface
Bloaty McBloatface 是用于类 unix 平台的二进制大小分析器。
你好,我是俞凡,在 Motorola 做过研发,当初在 Mavenir 做技术工作,对通信、网络、后端架构、云原生、DevOps、CICD、区块链、AI 等技术始终保持着浓重的趣味,平时喜爱浏览、思考,置信继续学习、一生成长,欢送一起交流学习。\
微信公众号:DeepNoMind
本文由 mdnice 多平台公布