Skip to main content

cpp-coding-standards

基于C++核心指南(isocpp.github.io)的C++编码标准。在编写、审查或重构C++代码时使用,以强制实施现代、安全和惯用的实践。

Jump to install

Source facts

Repository
affaan-m/ECC
Last source activity
March 3, 2026 at 06:28
Detected SKILL.md language
Chinese
Stars
264,820
Forks
39,570

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
cpp-coding-standards
description
基于C++核心指南(isocpp.github.io)的C++编码标准。在编写、审查或重构C++代码时使用,以强制实施现代、安全和惯用的实践。
origin
ECC
# C++ 编码标准(C++ 核心准则) 源自 [C++ 核心准则](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines) 的现代 C++(C++17/20/23)综合编码标准。强制执行类型安全、资源安全、不变性和清晰性。 ## 何时使用 * 编写新的 C++ 代码(类、函数、模板) * 审查或重构现有的 C++ 代码 * 在 C++ 项目中做出架构决策 * 在 C++ 代码库中强制执行一致的风格 * 在语言特性之间做出选择(例如,`enum` 对比 `enum class`,原始指针对比智能指针) ### 何时不应使用 * 非 C++ 项目 * 无法采用现代 C++ 特性的遗留 C 代码库 * 特定准则与硬件限制冲突的嵌入式/裸机环境(选择性适配) ## 贯穿性原则 这些主题在整个准则中反复出现,并构成了基础: 1. **处处使用 RAII** (P.8, R.1, E.6, CP.20):将资源生命周期绑定到对象生命周期 2. **默认为不可变性** (P.10, Con.1-5, ES.25):从 `const`/`constexpr` 开始;可变性是例外 3. **类型安全** (P.4, I.4, ES.46-49, Enum.3):使用类型系统在编译时防止错误 4. **表达意图** (P.3, F.1, NL.1-2, T.10):名称、类型和概念应传达目的 5. **最小化复杂性** (F.2-3, ES.5, Per.4-5):简单的代码就是正确的代码 6. **值语义优于指针语义** (C.10, R.3-5, F.20, CP.31):优先按值返回和作用域对象 ## 哲学与接口 (P.\*, I.\*) ### 关键规则 | 规则 | 摘要 | |------|---------| | **P.1** | 直接在代码中表达想法 | | **P.3** | 表达意图 | | **P.4** | 理想情况下,程序应是静态类型安全的 | | **P.5** | 优先编译时检查而非运行时检查 | | **P.8** | 不要泄漏任何资源 | | **P.10** | 优先不可变数据而非可变数据 | | **I.1** | 使接口明确 | | **I.2** | 避免非 const 全局变量 | | **I.4** | 使接口精确且强类型化 | | **I.11** | 切勿通过原始指针或引用转移所有权 | | **I.23** | 保持函数参数数量少 | ### 应该做 ```cpp // P.10 + I.4: Immutable, strongly typed interface struct Temperature { double kelvin; }; Temperature boil(const Temperature& water); ``` ### 不应该做 ```cpp // Weak interface: unclear ownership, unclear units double boil(double* temp); // Non-const global variable int g_counter = 0; // I.2 violation ``` ## 函数 (F.\*) ### 关键规则 | 规则 | 摘要 | |------|---------| | **F.1** | 将有意义的操作打包为精心命名的函数 | | **F.2** | 函数应执行单一逻辑操作 | | **F.3** | 保持函数简短简单 | | **F.4** | 如果函数可能在编译时求值,则将其声明为 `constexpr` | | **F.6** | 如果你的函数绝不能抛出异常,则将其声明为 `noexcept` | | **F.8** | 优先纯函数 | | **F.16** | 对于 "输入" 参数,按值传递廉价可复制类型,其他类型通过 `const&` 传递 | | **F.20** | 对于 "输出" 值,优先返回值而非输出参数 | | **F.21** | 要返回多个 "输出" 值,优先返回结构体 | | **F.43** | 切勿返回指向局部对象的指针或引用 | ### 参数传递 ```cpp // F.16: Cheap types by value, others by const& void print(int x); // cheap: by value void analyze(const std::string& data); // expensive: by const& void transform(std::string s); // sink: by value (will move) // F.20 + F.21: Return values, not output parameters struct ParseResult { std::string token; int position; }; ParseResult parse(std::string_view input); // GOOD: return struct // BAD: output parameters void parse(std::string_view input, std::string& token, int& pos); // avoid this ``` ### 纯函数和 constexpr ```cpp // F.4 + F.8: Pure, constexpr where possible constexpr int factorial(int n) noexcept { return (n <= 1) ? 1 : n * factorial(n - 1); } static_assert(factorial(5) == 120); ``` ### 反模式 * 从函数返回 `T&&` (F.45) * 使用 `va_arg` / C 风格可变参数 (F.55) * 在传递给其他线程的 lambda 中通过引用捕获 (F.53) * 返回 `const T`,这会抑制移动语义 (F.49) ## 类与类层次结构 (C.\*) ### 关键规则 | 规则 | 摘要 | |------|---------| | **C.2** | 如果存在不变式,使用 `class`;如果数据成员独立变化,使用 `struct` | | **C.9** | 最小化成员的暴露 | | **C.20** | 如果你能避免定义默认操作,就这么做(零规则) | | **C.21** | 如果你定义或 `=delete` 任何拷贝/移动/析构函数,则处理所有(五规则) | | **C.35** | 基类析构函数:公开虚函数或受保护非虚函数 | | **C.41** | 构造函数应创建完全初始化的对象 | | **C.46** | 将单参数构造函数声明为 `explicit` | | **C.67** | 多态类应禁止公开拷贝/移动 | | **C.128** | 虚函数:精确指定 `virtual`、`override` 或 `final` 中的一个 | ### 零规则 ```cpp // C.20: Let the compiler generate special members struct Employee { std::string name; std::string department; int id; // No destructor, copy/move constructors, or assignment operators needed }; ``` ### 五规则 ```cpp // C.21: If you must manage a resource, define all five class Buffer { public: explicit Buffer(std::size_t size) : data_(std::make_unique<char[]>(size)), size_(size) {} ~Buffer() = default; Buffer(const Buffer& other) : data_(std::make_unique<char[]>(other.size_)), size_(other.size_) { std::copy_n(other.data_.get(), size_, data_.get()); } Buffer& operator=(const Buffer& other) { if (this != &other) { auto new_data = std::make_unique<char[]>(other.size_); std::copy_n(other.data_.get(), other.size_, new_data.get()); data_ = std::move(new_data); size_ = other.size_; } return *this; } Buffer(Buffer&&) noexcept = default; Buffer& operator=(Buffer&&) noexcept = default; private: std::unique_ptr<char[]> data_; std::size_t size_; }; ``` ### 类层次结构 ```cpp // C.35 + C.128: Virtual destructor, use override class Shape { public: virtual ~Shape() = default; virtual double area() const = 0; // C.121: pure interface }; class Circle : public Shape { public: explicit Circle(double r) : radius_(r) {} double area() const override { return 3.14159 * radius_ * radius_; } private: double radius_; }; ``` ### 反模式 * 在构造函数/析构函数中调用虚函数 (C.82) * 在非平凡类型上使用 `memset`/`memcpy` (C.90) * 为虚函数和重写函数提供不同的默认参数 (C.140) * 将数据成员设为 `const` 或引用,这会抑制移动/拷贝 (C.12) ## 资源管理 (R.\*) ### 关键规则 | 规则 | 摘要 | |------|---------| | **R.1** | 使用 RAII 自动管理资源 | | **R.3** | 原始指针 (`T*`) 是非拥有的 | | **R.5** | 优先作用域对象;不要不必要地在堆上分配 | | **R.10** | 避免 `malloc()`/`free()` | | **R.11** | 避免显式调用 `new` 和 `delete` | | **R.20** | 使用 `unique_ptr` 或 `shared_ptr` 表示所有权 | | **R.21** | 除非共享所有权,否则优先 `unique_ptr` 而非 `shared_ptr` | | **R.22** | 使用 `make_shared()` 来创建 `shared_ptr` | ### 智能指针使用 ```cpp // R.11 + R.20 + R.21: RAII with smart pointers auto widget = std::make_unique<Widget>("config"); // unique ownership auto cache = std::make_shared<Cache>(1024); // shared ownership // R.3: Raw pointer = non-owning observer void render(const Widget* w) { // does NOT own w if (w) w->draw(); } render(widget.get()); ``` ### RAII 模式 ```cpp // R.1: Resource acquisition is initialization class FileHandle { public: explicit FileHandle(const std::string& path) : handle_(std::fopen(path.c_str(), "r")) { if (!handle_) throw std::runtime_error("Failed to open: " + path); } ~FileHandle() { if (handle_) std::fclose(handle_); } FileHandle(const FileHandle&) = delete; FileHandle& operator=(const FileHandle&) = delete; FileHandle(FileHandle&& other) noexcept : handle_(std::exchange(other.handle_, nullptr)) {} FileHandle& operator=(FileHandle&& other) noexcept { if (this != &other) { if (handle_) std::fclose(handle_); handle_ = std::exchange(other.handle_, nullptr); } return *this; } private: std::FILE* handle_; }; ``` ### 反模式 * 裸 `new`/`delete` (R.11) * C++ 代码中的 `malloc()`/`free()` (R.10) * 在单个表达式中进行多次资源分配 (R.13 -- 异常安全风险) * 在 `unique_ptr` 足够时使用 `shared_ptr` (R.21) ## 表达式与语句 (ES.\*) ### 关键规则 | 规则 | 摘要 | |------|---------| | **ES.5** | 保持作用域小 | | **ES.20** | 始终初始化对象 | | **ES.23** | 优先 `{}` 初始化语法 | | **ES.25** | 除非打算修改,否则将对象声明为 `const` 或 `constexpr` | | **ES.28** | 使用 lambda 进行 `const` 变量的复杂初始化 | | **ES.45** | 避免魔法常量;使用符号常量 | | **ES.46** | 避免有损的算术转换 | | **ES.47** | 使用 `nullptr` 而非 `0` 或 `NULL` | | **ES.48** | 避免强制类型转换 | | **ES.50** | 不要丢弃 `const` | ### 初始化 ```cpp // ES.20 + ES.23 + ES.25: Always initialize, prefer {}, default to const const int max_retries{3}; const std::string name{"widget"}; const std::vector<int> primes{2, 3, 5, 7, 11}; // ES.28: Lambda for complex const initialization const auto config = [&] { Config c; c.timeout = std::chrono::seconds{30}; c.retries = max_retries; c.verbose = debug_mode; return c; }(); ``` ### 反模式 * 未初始化的变量 (ES.20) * 使用 `0` 或 `NULL` 作为指针 (ES.47 -- 使用 `nullptr`) * C 风格强制类型转换 (ES.48 -- 使用 `static_cast`、`const_cast` 等) * 丢弃 `const` (ES.50) * 没有命名常量的魔法数字 (ES.45) * 混合有符号和无符号算术 (ES.100) * 在嵌套作用域中重用名称 (ES.12) ## 错误处理 (E.\*) ### 关键规则 | 规则 | 摘要 |
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub