// Weak interface: unclear ownership, unclear units
doubleboil(double*temp);// Non-const global variable
intg_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
切勿返回指向局部对象的指针或引用
参数传递
// F.16: Cheap types by value, others by const&
voidprint(intx);// cheap: by value
voidanalyze(conststd::string&data);// expensive: by const&
voidtransform(std::strings);// sink: by value (will move)
// F.20 + F.21: Return values, not output parameters
structParseResult{std::stringtoken;intposition;};ParseResultparse(std::string_viewinput);// GOOD: return struct
// BAD: output parameters
voidparse(std::string_viewinput,std::string&token,int&pos);// avoid this
纯函数和 constexpr
// F.4 + F.8: Pure, constexpr where possible
constexprintfactorial(intn)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 中的一个
零规则
// C.20: Let the compiler generate special members
structEmployee{std::stringname;std::stringdepartment;intid;// No destructor, copy/move constructors, or assignment operators needed
};
五规则
// C.21: If you must manage a resource, define all five
classBuffer{public:explicitBuffer(std::size_tsize):data_(std::make_unique<char[]>(size)),size_(size){}~Buffer()=default;Buffer(constBuffer&other):data_(std::make_unique<char[]>(other.size_)),size_(other.size_){std::copy_n(other.data_.get(),size_,data_.get());}Buffer&operator=(constBuffer&other){if(this!=&other){autonew_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_tsize_;};
类层次结构
// C.35 + C.128: Virtual destructor, use override
classShape{public:virtual~Shape()=default;virtualdoublearea()const=0;// C.121: pure interface
};classCircle:publicShape{public:explicitCircle(doubler):radius_(r){}doublearea()constoverride{return3.14159*radius_*radius_;}private:doubleradius_;};
反模式
在构造函数/析构函数中调用虚函数 (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
智能指针使用
// R.11 + R.20 + R.21: RAII with smart pointers
autowidget=std::make_unique<Widget>("config");// unique ownership
autocache=std::make_shared<Cache>(1024);// shared ownership
// R.3: Raw pointer = non-owning observer
voidrender(constWidget*w){// does NOT own w
if(w)w->draw();}render(widget.get());
RAII 模式
// R.1: Resource acquisition is initialization
classFileHandle{public:explicitFileHandle(conststd::string&path):handle_(std::fopen(path.c_str(),"r")){if(!handle_)throwstd::runtime_error("Failed to open: "+path);}~FileHandle(){if(handle_)std::fclose(handle_);}FileHandle(constFileHandle&)=delete;FileHandle&operator=(constFileHandle&)=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_;};
// E.14 + E.15: Custom exception types, throw by value, catch by reference
classAppError:publicstd::runtime_error{public:usingstd::runtime_error::runtime_error;};classNetworkError:publicAppError{public:NetworkError(conststd::string&msg,intcode):AppError(msg),status_code(code){}intstatus_code;};voidfetch_data(conststd::string&url){// E.2: Throw to signal failure
throwNetworkError("connection refused",503);}voidrun(){try{fetch_data("https://api.example.com");}catch(constNetworkError&e){log_error(e.what(),e.status_code);}catch(constAppError&e){log_error(e.what());}// E.17: Don't catch everything here -- let unexpected errors propagate
}
反模式
抛出内置类型,如 int 或字符串字面量 (E.14)
按值捕获(有切片风险) (E.15)
静默吞掉错误的空 catch 块
使用异常进行流程控制 (E.3)
基于全局状态(如 errno)的错误处理 (E.28)
常量与不可变性 (Con.*)
所有规则
规则
摘要
Con.1
默认情况下,使对象不可变
Con.2
默认情况下,使成员函数为 const
Con.3
默认情况下,传递指向 const 的指针和引用
Con.4
对构造后不改变的值使用 const
Con.5
对可在编译时计算的值使用 constexpr
// Con.1 through Con.5: Immutability by default
classSensor{public:explicitSensor(std::stringid):id_(std::move(id)){}// Con.2: const member functions by default
conststd::string&id()const{returnid_;}doublelast_reading()const{returnreading_;}// Only non-const when mutation is required
voidrecord(doublevalue){reading_=value;}private:conststd::stringid_;// Con.4: never changes after construction
doublereading_{0.0};};// Con.3: Pass by const reference
voiddisplay(constSensor&s){std::cout<<s.id()<<": "<<s.last_reading()<<'\n';}// Con.5: Compile-time constants
constexprdoublePI=3.14159265358979;constexprintMAX_SENSORS=256;
并发与并行 (CP.*)
关键规则
规则
摘要
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
除非绝对必要,否则不要使用无锁编程
安全加锁
// CP.20 + CP.44: RAII locks, always named
classThreadSafeQueue{public:voidpush(intvalue){std::lock_guard<std::mutex>lock(mutex_);// CP.44: named!
queue_.push(value);cv_.notify_one();}intpop(){std::unique_lock<std::mutex>lock(mutex_);// CP.42: Always wait with a condition
cv_.wait(lock,[this]{return!queue_.empty();});constintvalue=queue_.front();queue_.pop();returnvalue;}private:std::mutexmutex_;// CP.50: mutex with its data
std::condition_variablecv_;std::queue<int>queue_;};
多个互斥量
// CP.21: std::scoped_lock for multiple mutexes (deadlock-free)
voidtransfer(Account&from,Account&to,doubleamount){std::scoped_locklock(from.mutex_,to.mutex_);from.balance_-=amount;to.balance_+=amount;}
// 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>namespaceproject::module{classWidget{public:explicitWidget(std::stringname);conststd::string&name()const;private:std::stringname_;};}// namespace project::module
#endif // PROJECT_MODULE_WIDGET_H
命名约定
// NL.8 + NL.10: Consistent underscore_style
namespacemy_project{constexprintmax_buffer_size=4096;// NL.9: not ALL_CAPS (it's not a macro)
classtcp_connection{// underscore_style class
public:voidsend_message(std::string_viewmsg);boolis_connected()const;private:std::stringhost_;// trailing underscore for members
intport_;};}// namespace my_project
反模式
在头文件的全局作用域内使用 using namespace std; (SF.7)
依赖包含顺序的头文件 (SF.10, SF.11)
匈牙利命名法,如 strName、iCount (NL.5)
宏以外的事物使用 ALL_CAPS (NL.9)
性能 (Per.*)
关键规则
规则
摘要
Per.1
不要无故优化
Per.2
不要过早优化
Per.6
没有测量数据,不要断言性能
Per.7
设计时应考虑便于优化
Per.10
依赖静态类型系统
Per.11
将计算从运行时移至编译时
Per.19
以可预测的方式访问内存
指导原则
// Per.11: Compile-time computation where possible
constexprautolookup_table=[]{std::array<int,256>table{};for(inti=0;i<256;++i){table[i]=i*i;}returntable;}();// 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
反模式
在没有性能分析数据的情况下进行优化 (Per.1, Per.6)
选择“巧妙”的低级代码而非清晰的抽象 (Per.4, Per.5)
忽略数据布局和缓存行为 (Per.19)
快速参考检查清单
在标记 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)