CppCoreGuidelines P.7 尽早捕获运行时错误
23 December 2021
这条指南背后的原因是尽早发现错误,避免导致诡异的 bug。
以下程序传给 increment1
数组指针和数组大小作为参数。如果数组大小不小心写错了,会导致数组越界。程序可能会继续执行,但是随时可能运行奔溃。
#include <iostream> using namespace std; void increment1(int* p, int n) // bad: error-prone { for (int i = 0; i < n; ++i) ++p[i]; } void use1(int m) { const int n = 10; int a[n] = {}; for (int i = 10; i < m; i++) cout << a[i] << " "; cout << endl; increment1(a, m); // maybe typo, maybe m <= n is supposed // but assume that m == 20 for (int i = 10; i < m; i++) cout << a[i] << " "; cout << endl; } int main() { use1(20); return 0; }
24 0 0 10 0 15 1925183920 163 1207703186 32758 25 1 1 11 14 21 1925183921 164 1207703187 32759
以下代码用了 span
,但是数组大小还是分开设置了,依然会导致越界访问:
#include <iostream> #include <gsl/gsl> using namespace std; using namespace gsl; void increment2(span<int> p) { for (int& x : p) ++x; } void use2(int m) { const int n = 10; int a[n] = {}; for (int i = 10; i < m; i++) cout << a[i] << " "; cout << endl; increment2({a, m}); // maybe typo, maybe m <= n is supposed for (int i = 10; i < m; i++) cout << a[i] << " "; cout << endl; } int main() { use2(20); return 0; }
0 0 -2018306080 32758 24 0 0 10 0 19 1 1 21 1 281016609 124 1 11 18 21
直接传递一个带数组长度信息的 span
给 increment2
就不会出错了:
#include <iostream> #include <gsl/gsl> using namespace std; using namespace gsl; void increment2(span<int> p) { for (int& x : p) ++x; } void use2(int m) { const int n = 10; int a[n] = {}; for (int i = 0; i < m; i++) cout << a[i] << " "; cout << endl; increment2(a); // the number of elements of a need not be repeated for (int i = 0; i < m; i++) cout << a[i] << " "; cout << endl; } int main() { use2(20); return 0; }
0 0 0 0 0 0 0 0 0 0 0 0 1600133072 32758 24 0 0 10 0 19 1 1 1 1 1 1 1 1 1 1 0 0 10 0 1119876608 149 0 10 18 20
以下代码定义了 Date
对象,在 user2
中,获取到了 Date
却又把它转成
string
。之后,又用 extract_date
把 string
转成 Date
。做了一次多余的数据转换操作,实在没有必要。
Date read_date(istream& is); // read date from istream Date extract_date(const string& s); // extract date from string void user1(const string& date) // manipulate date { auto d = extract_date(date); // ... } void user2() { Date d = read_date(cin); // ... user1(d.to_string()); // ... }
注意事项:
- 避免多余的、过早的检查
- 避免在
O(1)
算法中添加O(n)
的检查 - 查看指针和数组:是否进行范围检测,检测是否重复多多余?
- 查看类型转换:能否避免窄化转换?
- 查看未检测的输入值
- 查看结构化数据是否又转成字符串使用?