CppCoreGuidelines P.6 不能在编译时检查的,要能在运行时检查
23 December 2021
如果程序里有很难检查到的错误的话,可能会在特定的条件下,导致莫明其妙的错误。因此要尽量在编译时和运行时检测到这些错误。当然,错误检测是需要代价的,如计算资源、计算时间等。
例子:
#include <iostream> using namespace std; void f(int* p) { cout << p[50]; } void g(int n) { // bad: the number of elements is not passed to f() f(new int[n]); } int main() { g(3); return 0; }
-2043739824
以上代码打印出了一个莫明其妙的数字。因为数组大小没有传给 f
,在 f
中又可以访问 p
的任意元素,超出了数组的范围。
#include <iostream> using namespace std; void f2(int* p, int n) { cout << p[n]; } void g2(int n) { f2(new int[n], 10); // bad: a wrong number of elements can be passed to f() } int main() { g2(3); return 0; }
1647575376
上面代码虽然添加了数组大小作为参数,但是这个参数的值可以是任何数字,一旦数值给错,就访问越界了,所以也不安全。
#include <iostream> #include <memory> using namespace std; void f3(unique_ptr<int[]> p, int n) { cout << p[n]; } void g3(int n) { f3(make_unique<int[]>(n), 10); // bad: pass ownership and size separately } int main() { g3(3); return 0; }
-1123024560
独占指针( unique_ptr
)可以用来传递指针所有权。但是上面还是单独传递了数组大小参数给 f3
。错误的参数,依然会导致越界访问数组。
#include <iostream> #include <memory> #include <vector> #include <gsl/gsl> using namespace std; void f4(vector<int>& v) { cout << v[5]; } void f5(gsl::span<int> v) { cout << v[5]; } void g3(int n) { vector<int> v(n); f4(v); // pass a reference, retain ownership f5(gsl::span<int>{v}); // pass a view, retain ownership } int main() { g3(9); return 0; }
0
以上代码,通过引用和 span
视图的方式传递参数,数组大小是 span
对象自身包含的数据,可以在运行时检查是否越界访问了。
以下代码使用 vector
对象能够传递所有权,并且同时保留了大小信息:
#include <iostream> #include <vector> #include <gsl/gsl> using namespace std; using namespace gsl; vector<int> f5(int n) // OK: move { vector<int> v(n); // ... initialize v ... return v; } unique_ptr<int[]> f6(int n) // bad: loses n { auto p = make_unique<int[]>(n); // ... initialize *p ... return p; } owner<int*> f7(int n) // bad: loses n and we might forget to delete { owner<int*> p = new int[n]; // ... initialize *p ... return p; } int main() { auto v5 = f5(5); cout << v5.size() << endl; cout << v5.at(4) << endl; auto v6 = f6(5); cout << v6[6] << endl; // out of range auto v7 = f7(5); cout << v7[6] << endl; // out of range delete v7; return 0; }
5 0 -1355022000 -1554522460