cpp-coding-standards

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

C++ Coding Standards (C++ Core Guidelines)

C++编码标准(C++ Core Guidelines)

Comprehensive coding standards for modern C++ (C++17/20/23) derived from the C++ Core Guidelines. Enforces type safety, resource safety, immutability, and clarity.
这份针对现代C++(C++17/20/23)的全面编码标准,源自C++ Core Guidelines,旨在强化类型安全、资源安全、不可变性与代码清晰度。

When to Use

适用场景

  • Writing new C++ code (classes, functions, templates)
  • Reviewing or refactoring existing C++ code
  • Making architectural decisions in C++ projects
  • Enforcing consistent style across a C++ codebase
  • Choosing between language features (e.g.,
    enum
    vs
    enum class
    , raw pointer vs smart pointer)
  • 编写新的C++代码(类、函数、模板)
  • 评审或重构现有C++代码
  • 在C++项目中做架构决策
  • 在C++代码库中推行一致的编码风格
  • 选择语言特性(例如
    enum
    enum class
    对比、原生指针与智能指针对比)

When NOT to Use

不适用场景

  • Non-C++ projects
  • Legacy C codebases that cannot adopt modern C++ features
  • Embedded/bare-metal contexts where specific guidelines conflict with hardware constraints (adapt selectively)
  • 非C++项目
  • 无法适配现代C++特性的遗留C代码库
  • 特定规则与硬件约束冲突的嵌入式/裸机环境(可选择性调整)

Cross-Cutting Principles

跨领域原则

These themes recur across the entire guidelines and form the foundation:
  1. RAII everywhere (P.8, R.1, E.6, CP.20): Bind resource lifetime to object lifetime
  2. Immutability by default (P.10, Con.1-5, ES.25): Start with
    const
    /
    constexpr
    ; mutability is the exception
  3. Type safety (P.4, I.4, ES.46-49, Enum.3): Use the type system to prevent errors at compile time
  4. Express intent (P.3, F.1, NL.1-2, T.10): Names, types, and concepts should communicate purpose
  5. Minimize complexity (F.2-3, ES.5, Per.4-5): Simple code is correct code
  6. Value semantics over pointer semantics (C.10, R.3-5, F.20, CP.31): Prefer returning by value and scoped objects
这些核心主题贯穿所有准则,是标准的基础:
  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):优先按值返回与作用域内对象

Philosophy & Interfaces (P., I.)

设计理念与接口(P.、I.

Key Rules

核心规则

RuleSummary
P.1Express ideas directly in code
P.3Express intent
P.4Ideally, a program should be statically type safe
P.5Prefer compile-time checking to run-time checking
P.8Don't leak any resources
P.10Prefer immutable data to mutable data
I.1Make interfaces explicit
I.2Avoid non-const global variables
I.4Make interfaces precisely and strongly typed
I.11Never transfer ownership by a raw pointer or reference
I.23Keep the number of function arguments low
规则摘要
P.1直接用代码表达设计思路
P.3清晰表达代码意图
P.4程序理想状态是静态类型安全的
P.5优先编译期检查而非运行期检查
P.8禁止资源泄漏
P.10优先使用不可变数据而非可变数据
I.1接口需显式定义
I.2避免非const全局变量
I.4接口需精确且强类型化
I.11绝不要通过原生指针或引用转移所有权
I.23减少函数参数数量

DO

正确示例

cpp
// P.10 + I.4: Immutable, strongly typed interface
struct Temperature {
    double kelvin;
};

Temperature boil(const Temperature& water);
cpp
// P.10 + I.4: 不可变、强类型接口
struct Temperature {
    double kelvin;
};

Temperature boil(const Temperature& water);

DON'T

错误示例

cpp
// Weak interface: unclear ownership, unclear units
double boil(double* temp);

// Non-const global variable
int g_counter = 0;  // I.2 violation
cpp
// 弱接口:所有权模糊、单位不明确
double boil(double* temp);

// 非const全局变量
int g_counter = 0;  // 违反I.2规则

Functions (F.*)

函数(F.*)

Key Rules

核心规则

RuleSummary
F.1Package meaningful operations as carefully named functions
F.2A function should perform a single logical operation
F.3Keep functions short and simple
F.4If a function might be evaluated at compile time, declare it
constexpr
F.6If your function must not throw, declare it
noexcept
F.8Prefer pure functions
F.16For "in" parameters, pass cheaply-copied types by value and others by
const&
F.20For "out" values, prefer return values to output parameters
F.21To return multiple "out" values, prefer returning a struct
F.43Never return a pointer or reference to a local object
规则摘要
F.1将有意义的操作封装为命名清晰的函数
F.2一个函数应只执行单一逻辑操作
F.3函数应简短简洁
F.4若函数可在编译期计算,声明为
constexpr
F.6若函数绝对不能抛出异常,声明为
noexcept
F.8优先使用纯函数
F.16对于"输入"参数,轻量复制类型按值传递,其他类型按
const&
传递
F.20对于"输出"值,优先返回值而非输出参数
F.21若要返回多个"输出"值,优先返回结构体
F.43绝不要返回局部对象的指针或引用

Parameter Passing

参数传递

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
cpp
// F.16: 轻量类型按值传递,其他按const&
void print(int x);                           // 轻量:按值
void analyze(const std::string& data);       // 重量级:按const&
void transform(std::string s);               // 接收端:按值(会触发移动)

// F.20 + F.21: 返回值而非输出参数
struct ParseResult {
    std::string token;
    int position;
};

ParseResult parse(std::string_view input);   // 推荐:返回结构体

// 不推荐:输出参数
void parse(std::string_view input,
           std::string& token, int& pos);    // 避免这种写法

Pure Functions and constexpr

纯函数与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);
cpp
// F.4 + F.8: 尽可能使用纯函数与constexpr
constexpr int factorial(int n) noexcept {
    return (n <= 1) ? 1 : n * factorial(n - 1);
}

static_assert(factorial(5) == 120);

Anti-Patterns

反模式

  • Returning
    T&&
    from functions (F.45)
  • Using
    va_arg
    / C-style variadics (F.55)
  • Capturing by reference in lambdas passed to other threads (F.53)
  • Returning
    const T
    which inhibits move semantics (F.49)
  • 从函数返回
    T&&
    (F.45)
  • 使用
    va_arg
    /C风格可变参数(F.55)
  • 在传递给其他线程的lambda中按引用捕获变量(F.53)
  • 返回
    const T
    会抑制移动语义(F.49)

Classes & Class Hierarchies (C.*)

类与类层次结构(C.*)

Key Rules

核心规则

RuleSummary
C.2Use
class
if invariant exists;
struct
if data members vary independently
C.9Minimize exposure of members
C.20If you can avoid defining default operations, do (Rule of Zero)
C.21If you define or
=delete
any copy/move/destructor, handle them all (Rule of Five)
C.35Base class destructor: public virtual or protected non-virtual
C.41A constructor should create a fully initialized object
C.46Declare single-argument constructors
explicit
C.67A polymorphic class should suppress public copy/move
C.128Virtual functions: specify exactly one of
virtual
,
override
, or
final
规则摘要
C.2存在不变式时用
class
;数据成员独立变化时用
struct
C.9最小化成员的可见性
C.20若能避免定义默认操作,就不要定义(零规则)
C.21若定义或
=delete
任何复制/移动/析构函数,需处理全部五个(五规则)
C.35基类析构函数:要么是public virtual,要么是protected non-virtual
C.41构造函数应创建完全初始化的对象
C.46单参数构造函数需声明为
explicit
C.67多态类应禁用公开的复制/移动操作
C.128虚函数:必须明确指定
virtual
override
final
中的一个

Rule of Zero

零规则

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.20:让编译器自动生成特殊成员函数
struct Employee {
    std::string name;
    std::string department;
    int id;
    // 无需定义析构函数、复制/移动构造函数或赋值运算符
};

Rule of Five

五规则

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.21:若必须管理资源,需定义全部五个函数
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_;
};

Class Hierarchy

类层次结构

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_;
};
cpp
// C.35 + C.128:虚析构函数,使用override
class Shape {
public:
    virtual ~Shape() = default;
    virtual double area() const = 0;  // C.121:纯接口
};

class Circle : public Shape {
public:
    explicit Circle(double r) : radius_(r) {}
    double area() const override { return 3.14159 * radius_ * radius_; }

private:
    double radius_;
};

Anti-Patterns

反模式

  • Calling virtual functions in constructors/destructors (C.82)
  • Using
    memset
    /
    memcpy
    on non-trivial types (C.90)
  • Providing different default arguments for virtual function and overrider (C.140)
  • Making data members
    const
    or references, which suppresses move/copy (C.12)
  • 在构造函数/析构函数中调用虚函数(C.82)
  • 在非平凡类型上使用
    memset
    /
    memcpy
    (C.90)
  • 为虚函数与重写函数提供不同的默认参数(C.140)
  • 将数据成员设为
    const
    或引用,这会抑制移动/复制操作(C.12)

Resource Management (R.*)

资源管理(R.*)

Key Rules

核心规则

RuleSummary
R.1Manage resources automatically using RAII
R.3A raw pointer (
T*
) is non-owning
R.5Prefer scoped objects; don't heap-allocate unnecessarily
R.10Avoid
malloc()
/
free()
R.11Avoid calling
new
and
delete
explicitly
R.20Use
unique_ptr
or
shared_ptr
to represent ownership
R.21Prefer
unique_ptr
over
shared_ptr
unless sharing ownership
R.22Use
make_shared()
to make
shared_ptr
s
规则摘要
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

Smart Pointer Usage

智能指针用法

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());
cpp
// R.11 + R.20 + R.21:用智能指针实现RAII
auto widget = std::make_unique<Widget>("config");  // 独占所有权
auto cache  = std::make_shared<Cache>(1024);        // 共享所有权

// R.3:原生指针 = 非拥有型观察者
void render(const Widget* w) {  // 不拥有w
    if (w) w->draw();
}

render(widget.get());

RAII Pattern

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_;
};
cpp
// R.1:资源获取即初始化
class FileHandle {
public:
    explicit FileHandle(const std::string& path)
        : handle_(std::fopen(path.c_str(), "r")) {
        if (!handle_) throw std::runtime_error("打开失败: " + 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_;
};

Anti-Patterns

反模式

  • Naked
    new
    /
    delete
    (R.11)
  • malloc()
    /
    free()
    in C++ code (R.10)
  • Multiple resource allocations in a single expression (R.13 -- exception safety hazard)
  • shared_ptr
    where
    unique_ptr
    suffices (R.21)
  • new
    /
    delete
    (R.11)
  • 在C++代码中使用
    malloc()
    /
    free()
    (R.10)
  • 在单个表达式中多次分配资源(R.13——存在异常安全风险)
  • unique_ptr
    足够的场景下使用
    shared_ptr
    (R.21)

Expressions & Statements (ES.*)

表达式与语句(ES.*)

Key Rules

核心规则

RuleSummary
ES.5Keep scopes small
ES.20Always initialize an object
ES.23Prefer
{}
initializer syntax
ES.25Declare objects
const
or
constexpr
unless modification is intended
ES.28Use lambdas for complex initialization of
const
variables
ES.45Avoid magic constants; use symbolic constants
ES.46Avoid narrowing/lossy arithmetic conversions
ES.47Use
nullptr
rather than
0
or
NULL
ES.48Avoid casts
ES.50Don't cast away
const
规则摘要
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
限定

Initialization

初始化

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;
}();
cpp
// ES.20 + ES.23 + ES.25:始终初始化,优先用{},默认设为const
const int max_retries{3};
const std::string name{"widget"};
const std::vector<int> primes{2, 3, 5, 7, 11};

// ES.28:用lambda完成复杂const变量初始化
const auto config = [&] {
    Config c;
    c.timeout = std::chrono::seconds{30};
    c.retries = max_retries;
    c.verbose = debug_mode;
    return c;
}();

Anti-Patterns

反模式

  • Uninitialized variables (ES.20)
  • Using
    0
    or
    NULL
    as pointer (ES.47 -- use
    nullptr
    )
  • C-style casts (ES.48 -- use
    static_cast
    ,
    const_cast
    , etc.)
  • Casting away
    const
    (ES.50)
  • Magic numbers without named constants (ES.45)
  • Mixing signed and unsigned arithmetic (ES.100)
  • Reusing names in nested scopes (ES.12)
  • 未初始化变量(ES.20)
  • 0
    NULL
    作为指针(ES.47——应使用
    nullptr
  • C风格类型转换(ES.48——应使用
    static_cast
    const_cast
    等)
  • 移除
    const
    限定(ES.50)
  • 未命名的魔法数字(ES.45)
  • 混合有符号与无符号算术运算(ES.100)
  • 在嵌套作用域中重用名称(ES.12)

Error Handling (E.*)

错误处理(E.*)

Key Rules

核心规则

RuleSummary
E.1Develop an error-handling strategy early in a design
E.2Throw an exception to signal that a function can't perform its assigned task
E.6Use RAII to prevent leaks
E.12Use
noexcept
when throwing is impossible or unacceptable
E.14Use purpose-designed user-defined types as exceptions
E.15Throw by value, catch by reference
E.16Destructors, deallocation, and swap must never fail
E.17Don't try to catch every exception in every function
规则摘要
E.1在设计初期制定错误处理策略
E.2抛出异常以表示函数无法完成指定任务
E.6用RAII防止资源泄漏
E.12当无法或不允许抛出异常时,使用
noexcept
E.14使用专门设计的自定义类型作为异常
E.15按值抛出异常,按引用捕获异常
E.16析构函数、释放函数与swap绝不能失败
E.17不要在每个函数中都尝试捕获所有异常

Exception Hierarchy

异常层次结构

cpp
// E.14 + E.15: Custom exception types, throw by value, catch by reference
class AppError : public std::runtime_error {
public:
    using std::runtime_error::runtime_error;
};

class NetworkError : public AppError {
public:
    NetworkError(const std::string& msg, int code)
        : AppError(msg), status_code(code) {}
    int status_code;
};

void fetch_data(const std::string& url) {
    // E.2: Throw to signal failure
    throw NetworkError("connection refused", 503);
}

void run() {
    try {
        fetch_data("https://api.example.com");
    } catch (const NetworkError& e) {
        log_error(e.what(), e.status_code);
    } catch (const AppError& e) {
        log_error(e.what());
    }
    // E.17: Don't catch everything here -- let unexpected errors propagate
}
cpp
// E.14 + E.15:自定义异常类型,按值抛出、按引用捕获
class AppError : public std::runtime_error {
public:
    using std::runtime_error::runtime_error;
};

class NetworkError : public AppError {
public:
    NetworkError(const std::string& msg, int code)
        : AppError(msg), status_code(code) {}
    int status_code;
};

void fetch_data(const std::string& url) {
    // E.2:抛出异常表示失败
    throw NetworkError("连接被拒绝", 503);
}

void run() {
    try {
        fetch_data("https://api.example.com");
    } catch (const NetworkError& e) {
        log_error(e.what(), e.status_code);
    } catch (const AppError& e) {
        log_error(e.what());
    }
    // E.17:不要在这里捕获所有异常——让未预期的异常向上传播
}

Anti-Patterns

反模式

  • Throwing built-in types like
    int
    or string literals (E.14)
  • Catching by value (slicing risk) (E.15)
  • Empty catch blocks that silently swallow errors
  • Using exceptions for flow control (E.3)
  • Error handling based on global state like
    errno
    (E.28)
  • 抛出
    int
    或字符串字面量等内置类型(E.14)
  • 按值捕获异常(存在切片风险)(E.15)
  • 空catch块静默吞掉错误
  • 用异常做流程控制(E.3)
  • 基于
    errno
    等全局状态做错误处理(E.28)

Constants & Immutability (Con.*)

常量与不可变性(Con.*)

All Rules

全部规则

RuleSummary
Con.1By default, make objects immutable
Con.2By default, make member functions
const
Con.3By default, pass pointers and references to
const
Con.4Use
const
for values that don't change after construction
Con.5Use
constexpr
for values computable at compile time
cpp
// Con.1 through Con.5: Immutability by default
class Sensor {
public:
    explicit Sensor(std::string id) : id_(std::move(id)) {}

    // Con.2: const member functions by default
    const std::string& id() const { return id_; }
    double last_reading() const { return reading_; }

    // Only non-const when mutation is required
    void record(double value) { reading_ = value; }

private:
    const std::string id_;  // Con.4: never changes after construction
    double reading_{0.0};
};

// Con.3: Pass by const reference
void display(const Sensor& s) {
    std::cout << s.id() << ": " << s.last_reading() << '\n';
}

// Con.5: Compile-time constants
constexpr double PI = 3.14159265358979;
constexpr int MAX_SENSORS = 256;
规则摘要
Con.1默认将对象设为不可变
Con.2默认将成员函数设为
const
Con.3默认将指针和引用传递给
const
Con.4对构造后不再变化的值使用
const
Con.5对可在编译期计算的值使用
constexpr
cpp
// Con.1至Con.5:默认不可变
class Sensor {
public:
    explicit Sensor(std::string id) : id_(std::move(id)) {}

    // Con.2:默认const成员函数
    const std::string& id() const { return id_; }
    double last_reading() const { return reading_; }

    // 仅当需要修改时才使用非const
    void record(double value) { reading_ = value; }

private:
    const std::string id_;  // Con.4:构造后永不改变
    double reading_{0.0};
};

// Con.3:按const引用传递
void display(const Sensor& s) {
    std::cout << s.id() << ": " << s.last_reading() << '\n';
}

// Con.5:编译期常量
constexpr double PI = 3.14159265358979;
constexpr int MAX_SENSORS = 256;

Concurrency & Parallelism (CP.*)

并发与并行(CP.*)

Key Rules

核心规则

RuleSummary
CP.2Avoid data races
CP.3Minimize explicit sharing of writable data
CP.4Think in terms of tasks, rather than threads
CP.8Don't use
volatile
for synchronization
CP.20Use RAII, never plain
lock()
/
unlock()
CP.21Use
std::scoped_lock
to acquire multiple mutexes
CP.22Never call unknown code while holding a lock
CP.42Don't wait without a condition
CP.44Remember to name your
lock_guard
s and
unique_lock
s
CP.100Don't use lock-free programming unless you absolutely have to
规则摘要
CP.2避免数据竞争
CP.3最小化可写数据的显式共享
CP.4从任务而非线程的角度思考
CP.8不要用
volatile
做同步
CP.20使用RAII,绝不使用裸
lock()
/
unlock()
CP.21
std::scoped_lock
获取多个互斥锁
CP.22持有锁时绝不调用未知代码
CP.42不要无条件等待
CP.44务必为
lock_guard
unique_lock
命名
CP.100除非绝对必要,否则不要使用无锁编程

Safe Locking

安全加锁

cpp
// CP.20 + CP.44: RAII locks, always named
class ThreadSafeQueue {
public:
    void push(int value) {
        std::lock_guard<std::mutex> lock(mutex_);  // CP.44: named!
        queue_.push(value);
        cv_.notify_one();
    }

    int pop() {
        std::unique_lock<std::mutex> lock(mutex_);
        // CP.42: Always wait with a condition
        cv_.wait(lock, [this] { return !queue_.empty(); });
        const int value = queue_.front();
        queue_.pop();
        return value;
    }

private:
    std::mutex mutex_;             // CP.50: mutex with its data
    std::condition_variable cv_;
    std::queue<int> queue_;
};
cpp
// CP.20 + CP.44:RAII锁,必须命名
class ThreadSafeQueue {
public:
    void push(int value) {
        std::lock_guard<std::mutex> lock(mutex_);  // CP.44:必须命名!
        queue_.push(value);
        cv_.notify_one();
    }

    int pop() {
        std::unique_lock<std::mutex> lock(mutex_);
        // CP.42:必须带条件等待
        cv_.wait(lock, [this] { return !queue_.empty(); });
        const int value = queue_.front();
        queue_.pop();
        return value;
    }

private:
    std::mutex mutex_;             // CP.50:互斥锁与对应数据绑定
    std::condition_variable cv_;
    std::queue<int> queue_;
};

Multiple Mutexes

多互斥锁

cpp
// CP.21: std::scoped_lock for multiple mutexes (deadlock-free)
void transfer(Account& from, Account& to, double amount) {
    std::scoped_lock lock(from.mutex_, to.mutex_);
    from.balance_ -= amount;
    to.balance_ += amount;
}
cpp
// CP.21:用std::scoped_lock获取多个互斥锁(无死锁)
void transfer(Account& from, Account& to, double amount) {
    std::scoped_lock lock(from.mutex_, to.mutex_);
    from.balance_ -= amount;
    to.balance_ += amount;
}

Anti-Patterns

反模式

  • volatile
    for synchronization (CP.8 -- it's for hardware I/O only)
  • Detaching threads (CP.26 -- lifetime management becomes nearly impossible)
  • Unnamed lock guards:
    std::lock_guard<std::mutex>(m);
    destroys immediately (CP.44)
  • Holding locks while calling callbacks (CP.22 -- deadlock risk)
  • Lock-free programming without deep expertise (CP.100)
  • volatile
    做同步(CP.8——它仅用于硬件I/O)
  • 分离线程(CP.26——生命周期管理几乎失控)
  • 未命名的锁守卫:
    std::lock_guard<std::mutex>(m);
    会立即销毁(CP.44)
  • 持有锁时调用回调函数(CP.22——存在死锁风险)
  • 无专业知识却使用无锁编程(CP.100)

Templates & Generic Programming (T.*)

模板与泛型编程(T.*)

Key Rules

核心规则

RuleSummary
T.1Use templates to raise the level of abstraction
T.2Use templates to express algorithms for many argument types
T.10Specify concepts for all template arguments
T.11Use standard concepts whenever possible
T.13Prefer shorthand notation for simple concepts
T.43Prefer
using
over
typedef
T.120Use template metaprogramming only when you really need to
T.144Don't specialize function templates (overload instead)
规则摘要
T.1用模板提升抽象层级
T.2用模板为多种参数类型实现算法
T.10为所有模板参数指定concept
T.11尽可能使用标准concept
T.13简单concept优先使用简写语法
T.43优先使用
using
而非
typedef
T.120仅在真正需要时才使用模板元编程
T.144不要特化函数模板(优先重载)

Concepts (C++20)

Concept(C++20)

cpp
#include <concepts>

// T.10 + T.11: Constrain templates with standard concepts
template<std::integral T>
T gcd(T a, T b) {
    while (b != 0) {
        a = std::exchange(b, a % b);
    }
    return a;
}

// T.13: Shorthand concept syntax
void sort(std::ranges::random_access_range auto& range) {
    std::ranges::sort(range);
}

// Custom concept for domain-specific constraints
template<typename T>
concept Serializable = requires(const T& t) {
    { t.serialize() } -> std::convertible_to<std::string>;
};

template<Serializable T>
void save(const T& obj, const std::string& path);
cpp
#include <concepts>

// T.10 + T.11:用标准concept约束模板
template<std::integral T>
T gcd(T a, T b) {
    while (b != 0) {
        a = std::exchange(b, a % b);
    }
    return a;
}

// T.13:Concept简写语法
void sort(std::ranges::random_access_range auto& range) {
    std::ranges::sort(range);
}

// 针对特定领域约束的自定义concept
template<typename T>
concept Serializable = requires(const T& t) {
    { t.serialize() } -> std::convertible_to<std::string>;
};

template<Serializable T>
void save(const T& obj, const std::string& path);

Anti-Patterns

反模式

  • Unconstrained templates in visible namespaces (T.47)
  • Specializing function templates instead of overloading (T.144)
  • Template metaprogramming where
    constexpr
    suffices (T.120)
  • typedef
    instead of
    using
    (T.43)
  • 在可见命名空间中使用无约束模板(T.47)
  • 特化函数模板而非重载(T.144)
  • constexpr
    足够的场景下使用模板元编程(T.120)
  • typedef
    而非
    using
    (T.43)

Standard Library (SL.*)

标准库(SL.*)

Key Rules

核心规则

RuleSummary
SL.1Use libraries wherever possible
SL.2Prefer the standard library to other libraries
SL.con.1Prefer
std::array
or
std::vector
over C arrays
SL.con.2Prefer
std::vector
by default
SL.str.1Use
std::string
to own character sequences
SL.str.2Use
std::string_view
to refer to character sequences
SL.io.50Avoid
endl
(use
'\n'
--
endl
forces a flush)
cpp
// SL.con.1 + SL.con.2: Prefer vector/array over C arrays
const std::array<int, 4> fixed_data{1, 2, 3, 4};
std::vector<std::string> dynamic_data;

// SL.str.1 + SL.str.2: string owns, string_view observes
std::string build_greeting(std::string_view name) {
    return "Hello, " + std::string(name) + "!";
}

// SL.io.50: Use '\n' not endl
std::cout << "result: " << value << '\n';
规则摘要
SL.1代码文件用
.cpp
,接口文件用
.h
SL.2优先使用标准库而非其他库
SL.con.1优先使用
std::array
std::vector
而非C数组
SL.con.2默认优先使用
std::vector
SL.str.1
std::string
管理字符序列所有权
SL.str.2
std::string_view
引用字符序列
SL.io.50避免使用
endl
(用
'\n'
——
endl
会强制刷新缓冲区)
cpp
// SL.con.1 + SL.con.2:优先用vector/array而非C数组
const std::array<int, 4> fixed_data{1, 2, 3, 4};
std::vector<std::string> dynamic_data;

// SL.str.1 + SL.str.2:string拥有所有权,string_view仅引用
std::string build_greeting(std::string_view name) {
    return "Hello, " + std::string(name) + "!";
}

// SL.io.50:用'\n'而非endl
std::cout << "结果: " << value << '\n';

Enumerations (Enum.*)

枚举(Enum.*)

Key Rules

核心规则

RuleSummary
Enum.1Prefer enumerations over macros
Enum.3Prefer
enum class
over plain
enum
Enum.5Don't use ALL_CAPS for enumerators
Enum.6Avoid unnamed enumerations
cpp
// Enum.3 + Enum.5: Scoped enum, no ALL_CAPS
enum class Color { red, green, blue };
enum class LogLevel { debug, info, warning, error };

// BAD: plain enum leaks names, ALL_CAPS clashes with macros
enum { RED, GREEN, BLUE };           // Enum.3 + Enum.5 + Enum.6 violation
#define MAX_SIZE 100                  // Enum.1 violation -- use constexpr
规则摘要
Enum.1优先使用枚举而非宏
Enum.3优先使用
enum class
而非普通
enum
Enum.5枚举器不要用全大写
Enum.6避免未命名枚举
cpp
// Enum.3 + Enum.5:作用域枚举,不用全大写
enum class Color { red, green, blue };
enum class LogLevel { debug, info, warning, error };

// 不推荐:普通枚举会泄漏名称,全大写与宏冲突
enum { RED, GREEN, BLUE };           // 违反Enum.3、Enum.5、Enum.6规则
#define MAX_SIZE 100                  // 违反Enum.1规则——应使用constexpr

Source Files & Naming (SF., NL.)

源文件与命名(SF.、NL.

Key Rules

核心规则

RuleSummary
SF.1Use
.cpp
for code files and
.h
for interface files
SF.7Don't write
using namespace
at global scope in a header
SF.8Use
#include
guards for all
.h
files
SF.11Header files should be self-contained
NL.5Avoid encoding type information in names (no Hungarian notation)
NL.8Use a consistent naming style
NL.9Use ALL_CAPS for macro names only
NL.10Prefer
underscore_style
names
规则摘要
SF.1代码文件用
.cpp
,接口文件用
.h
SF.7不要在头文件的全局作用域中写
using namespace
SF.8所有
.h
文件都要使用包含保护
SF.11头文件应自包含
NL.5避免在名称中编码类型信息(不要用匈牙利命名法)
NL.8使用一致的命名风格
NL.9仅宏名称使用全大写
NL.10优先使用下划线分隔命名法

Header Guard

头文件保护

cpp
// SF.8: Include guard (or #pragma once)
#ifndef PROJECT_MODULE_WIDGET_H
#define PROJECT_MODULE_WIDGET_H

// SF.11: Self-contained -- include everything this header needs
#include <string>
#include <vector>

namespace project::module {

class Widget {
public:
    explicit Widget(std::string name);
    const std::string& name() const;

private:
    std::string name_;
};

}  // namespace project::module

#endif  // PROJECT_MODULE_WIDGET_H
cpp
// SF.8:包含保护(或#pragma once)
#ifndef PROJECT_MODULE_WIDGET_H
#define PROJECT_MODULE_WIDGET_H

// SF.11:自包含——包含该头文件所需的所有依赖
#include <string>
#include <vector>

namespace project::module {

class Widget {
public:
    explicit Widget(std::string name);
    const std::string& name() const;

private:
    std::string name_;
};

}  // namespace project::module

#endif  // PROJECT_MODULE_WIDGET_H

Naming Conventions

命名规范

cpp
// NL.8 + NL.10: Consistent underscore_style
namespace my_project {

constexpr int max_buffer_size = 4096;  // NL.9: not ALL_CAPS (it's not a macro)

class tcp_connection {                 // underscore_style class
public:
    void send_message(std::string_view msg);
    bool is_connected() const;

private:
    std::string host_;                 // trailing underscore for members
    int port_;
};

}  // namespace my_project
cpp
// NL.8 + NL.10:一致的下划线分隔命名
namespace my_project {

constexpr int max_buffer_size = 4096;  // NL.9:不用全大写(这不是宏)

class tcp_connection {                 // 下划线分隔的类名
public:
    void send_message(std::string_view msg);
    bool is_connected() const;

private:
    std::string host_;                 // 成员变量用后缀下划线
    int port_;
};

}  // namespace my_project

Anti-Patterns

反模式

  • using namespace std;
    in a header at global scope (SF.7)
  • Headers that depend on inclusion order (SF.10, SF.11)
  • Hungarian notation like
    strName
    ,
    iCount
    (NL.5)
  • ALL_CAPS for anything other than macros (NL.9)
  • 在头文件全局作用域中写
    using namespace std;
    (SF.7)
  • 依赖包含顺序的头文件(SF.10、SF.11)
  • 匈牙利命名法如
    strName
    iCount
    (NL.5)
  • 非宏名称使用全大写(NL.9)

Performance (Per.*)

性能(Per.*)

Key Rules

核心规则

RuleSummary
Per.1Don't optimize without reason
Per.2Don't optimize prematurely
Per.6Don't make claims about performance without measurements
Per.7Design to enable optimization
Per.10Rely on the static type system
Per.11Move computation from run time to compile time
Per.19Access memory predictably
规则摘要
Per.1无理由不要优化
Per.2不要过早优化
Per.6无性能测试数据不要做性能相关声明
Per.7设计时要考虑可优化性
Per.10依赖静态类型系统
Per.11将计算从运行期转移到编译期
Per.19可预测地访问内存

Guidelines

实践指南

cpp
// Per.11: Compile-time computation where possible
constexpr auto lookup_table = [] {
    std::array<int, 256> table{};
    for (int i = 0; i < 256; ++i) {
        table[i] = i * i;
    }
    return table;
}();

// Per.19: Prefer contiguous data for cache-friendliness
std::vector<Point> points;           // GOOD: contiguous
std::vector<std::unique_ptr<Point>> indirect_points; // BAD: pointer chasing
cpp
// Per.11:尽可能在编译期计算
constexpr auto lookup_table = [] {
    std::array<int, 256> table{};
    for (int i = 0; i < 256; ++i) {
        table[i] = i * i;
    }
    return table;
}();

// Per.19:优先使用连续数据以提升缓存友好性
std::vector<Point> points;           // 推荐:连续存储
std::vector<std::unique_ptr<Point>> indirect_points; // 不推荐:指针跳转

Anti-Patterns

反模式

  • Optimizing without profiling data (Per.1, Per.6)
  • Choosing "clever" low-level code over clear abstractions (Per.4, Per.5)
  • Ignoring data layout and cache behavior (Per.19)
  • 无性能分析数据就做优化(Per.1、Per.6)
  • 选择“巧妙”的底层代码而非清晰的抽象(Per.4、Per.5)
  • 忽略数据布局与缓存行为(Per.19)

Quick Reference Checklist

快速参考检查清单

Before marking C++ work complete:
  • No raw
    new
    /
    delete
    -- use smart pointers or RAII (R.11)
  • Objects initialized at declaration (ES.20)
  • Variables are
    const
    /
    constexpr
    by default (Con.1, ES.25)
  • Member functions are
    const
    where possible (Con.2)
  • enum class
    instead of plain
    enum
    (Enum.3)
  • nullptr
    instead of
    0
    /
    NULL
    (ES.47)
  • No narrowing conversions (ES.46)
  • No C-style casts (ES.48)
  • Single-argument constructors are
    explicit
    (C.46)
  • Rule of Zero or Rule of Five applied (C.20, C.21)
  • Base class destructors are public virtual or protected non-virtual (C.35)
  • Templates are constrained with concepts (T.10)
  • No
    using namespace
    in headers at global scope (SF.7)
  • Headers have include guards and are self-contained (SF.8, SF.11)
  • Locks use RAII (
    scoped_lock
    /
    lock_guard
    ) (CP.20)
  • Exceptions are custom types, thrown by value, caught by reference (E.14, E.15)
  • '\n'
    instead of
    std::endl
    (SL.io.50)
  • No magic numbers (ES.45)
在完成C++工作前,请检查:
  • 无裸
    new
    /
    delete
    ——使用智能指针或RAII(R.11)
  • 对象在声明时初始化(ES.20)
  • 变量默认设为
    const
    /
    constexpr
    (Con.1、ES.25)
  • 成员函数尽可能设为
    const
    (Con.2)
  • enum class
    替代普通
    enum
    (Enum.3)
  • nullptr
    替代
    0
    /
    NULL
    (ES.47)
  • 无窄化转换(ES.46)
  • 无C风格类型转换(ES.48)
  • 单参数构造函数是
    explicit
    (C.46)
  • 应用了零规则或五规则(C.20、C.21)
  • 基类析构函数是public virtual或protected non-virtual(C.35)
  • 模板用concept约束(T.10)
  • 头文件全局作用域中无
    using namespace
    (SF.7)
  • 头文件有包含保护且自包含(SF.8、SF.11)
  • 锁使用RAII(
    scoped_lock
    /
    lock_guard
    )(CP.20)
  • 异常是自定义类型,按值抛出、按引用捕获(E.14、E.15)
  • '\n'
    替代
    std::endl
    (SL.io.50)
  • 无魔法数字(ES.45)