C++ Core Guidelines (Bjarne Stroustrup Herb Sutter)
P: Philosophy
- 见名思意
- 尽量使用标准库
- 可以编译时检查的尽量编译时检查 比如使用static_assert等等
- 资源一定要释放
- const的合理使用
- 使用合适的工具以及一些工具库避免重复造轮子
P.1: Express ideas directly in code
所见即所得 代码要见名思意
class Date {
public:
Month month() const; // do
int month(); // don't
// ...
};
1. const 成员函数
2. 返回值问题 int可能隐士构造一个Month 直接返回Month更优
// bad example
void f(vector<string>& v)
{
string val;
cin >> val;
// ...
int index = -1; // bad, plus should use gsl::index
for (int i = 0; i < v.size(); ++i) {
if (v[i] == val) {
index = i;
break;
}
}
// ...
}
// good example
void f(vector<string>& v)
{
string val;
cin >> val;
// ...
auto p = find(begin(v), end(v), val); // better
// ...
}
1. 一个好的设计是表现出意图而不是做了什么
2. 编程人员需要知道基本的c++标准库或使用的库提供了哪些功能以及本身项目提供了哪些基础接口 尽量使用这些接口
3. 这里的第一个f函数不过是std::find的一个受限制版本
change_speed(double s); // bad: what does s signify?
// ...
change_speed(2.3);
change_speed(Speed s); // better: the meaning of s is specified
// ...
change_speed(2.3); // error: no unit
change_speed(23m / 10s); // meters per second
1. 第一个接收double不明确是接收的绝度速度还是delta速度差意义不明
Enforcement Very hard in general.
- 成员函数以及参数对const的使用
- casts的类型转换
- 尽量使用标准库而不是使用语言特性进行硬编码 参见第二个例子
P.2: Write in ISO Standard C++
尽量使用标准库
P.3: Express intent
表达意图
// bad
gsl::index i = 0;
while (i < v.size()) {
// ... do something with v[i] ...
}
// Better
for (const auto& x : v) { /* do something with the value of x */ }
for (auto& x : v) { /* modify x */ }
for_each(v, [](int x) { /* do something with the value of x */ });
for_each(par, v, [](int x) { /* do something with the value of x */ });
1. 第一种文档上这样描述的
1.1 便利v的元素进行处理的意图并没有表现出来
1.2 直接使用语言特性尽心编码 索引的实现细节被公开 可能会导致滥用
1.3 索引idx超出了循环范围 可能是是在循环体内部有意的 也可能是无意的 因此这里无法猜测确定其意图(1.1)
2. 最后一个foreach明确的表达了我们对v元素的顺序不关心
3. 还是尽量使用标准库吧
P.4: Ideally, a program should be statically type safe
静态类型安全 但是做到这一点基本不可能
以下几个方面可能是一系列问题的来源 后面有建议用一些其他技术代替
- unions(联合体) - use variant(c++17)
- casts(类型转换) - 使用模板或者其他方式减少使用
- array decay - 使用 span (from the GSL)
- range erros - 使用 span
- narrowing conversions - 尽量减少使用 在必要的地方使用 narrow 或者 narrow_cast (from the GSL)
P.5: Prefer compile-time checking to run-time checking
比起运行时检查 编译时检查跟优
// Int is an alias used for integers
int bits = 0; // don't: avoidable code
for (Int i = 1; i; i <<= 1)
++bits;
if (bits < 32)
cerr << "Int too small\n";
// Int is an alias used for integers
static_assert(sizeof(Int) >= 4); // do: compile-time check
1. 使用static_assert的编译时检查更好
2. 或者使用int32_t替代int
// bad
void read(int* p, int n); // read max n integers into *p
int a[100];
read(a, 1000); // bad, off the end
// better
void read(span<int> r); // read into the range of integers r
int a[100];
read(a); // better: let the compiler figure out the number of elements
Enforcement
- 检查指针参数
Look for pointer arguments.
- 检查运行时的range合法性检查
Look for run-time checks for range violations.
P.6: What cannot be checked at compile time should be checkable at run time
只能在运行时检查的不要再编译时检查
还是尽量使用标准库
// separately compiled, possibly dynamically loaded
extern void f(int* p);
void g(int n)
{
// bad: the number of elements is not passed to f()
f(new int[n]);
}
1. 没有传递大小
2. 可能没有delete
// separately compiled, possibly dynamically loaded
extern void f2(int* p, int n);
void g2(int n)
{
f2(new int[n], m); // bad: a wrong number of elements can be passed to f()
}
1. 传递了错误的大小
2. 可能没有delete
// Example, bad The standard library resource management pointers fail to pass the size when they point to an object
// 当标准库资源对象管理指针指向一个对象时无法传递大小 例子如下
// separately compiled, possibly dynamically loaded
// NB: this assumes the calling code is ABI-compatible, using a
// compatible C++ compiler and the same stdlib implementation
extern void f3(unique_ptr<int[]>, int n);
void g3(int n)
{
f3(make_unique<int[]>(n), m); // bad: pass ownership and size separately
}
//////////////////////////////////////////////////////////
vector<int> f5(int n) // OK: move
{
vector<int> v(n);
// ... initialize v ...
return v;
}
1.正确的OK
unique_ptr<int[]> f6(int n) // bad: loses n
{
auto p = make_unique<int[]>(n);
// ... initialize *p ...
return p;
}
1. 不能返回大小n
owner<int*> f7(int n) // bad: loses n and we might forget to delete
{
owner<int*> p = new int[n];
// ... initialize *p ...
return p;
}
1. 可能未delete泄露
// Example We need to pass the pointer and the number of elements as an integral object
// 正确的应该是将指针和大小当做一个完整对象传递 比如用vector span 等等标准库结构
extern void f4(vector<int>&); // separately compiled, possibly dynamically loaded
extern void f4(span<int>); // separately compiled, possibly dynamically loaded
// NB: this assumes the calling code is ABI-compatible, using a
// compatible C++ compiler and the same stdlib implementation
void g3(int n)
{
vector<int> v(n);
f4(v); // pass a reference, retain ownership
f4(span<int>{v}); // pass a view, retain ownership
}
P.7: Catch run-time errors early
尽早进行合法性检查
Enforcement
Look at pointers and arrays: Do range-checking early and not repeatedly
检查指针和数组的有效性
Look at conversions: Eliminate or mark narrowing conversions
尽量减少或者标记narrowing conversions
Look for unchecked values coming from input
检查输入合法性
Look for structured data (objects of classes with invariants) being converted into strings
检查转变成string的结构化数据(具有不变性的类对象)
P.8: Don’t leak any resources
一定要释放资源
Enforcement
- 检查指针
- 检查new和delete
- 检查一直的可以分配资源返回原始指针的操作比如fopen malloc strdup等操作
P.9: Don’t waste time or space
尽量高效
// Example, bad
void lower(zstring s)
{
for (int i = 0; i < strlen(s); ++i) s[i] = tolower(s[i]);
}
1. 每次循环都会对strlen求值 假设在lower函数不改变长度 可以在循环外部对strlen求值
P.10: Prefer immutable data to mutable data
相比可变数据优先选择不可变数据
P.11: Encapsulate messy constructs, rather than spreading through the code
封装混乱的结构 避免代码分散 纯看自己面向对象设计能力ggg
P.12: Use supporting tools as appropriate
适当使用辅助工具
P.13: Use support libraries as appropriate
适当使用合适的工具库