原因
在程序中留下难以检测的错误会导致崩溃和错误结果。
注意
理想情况下,我们在编译期和运行期捕获程序的所有错误(除了程序的逻辑错误)。在编译期捕获所有错误是不可能的,但是运行期捕获所有遗留的错误通常是难以承受的。但是在资源充足的条件下(分析程序、运行时检查、机器资源、时间),我们应该努力编写原则上可以检查的程序。
反例
// 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]);
}
这里,有一个至关重要的信息(元素的数量)被彻底掩盖了,这导致静态分析已经不可行,而且当f()
是一个二进制程序接口(ABI)时动态检查可能非常困难,因此我们无法“检测”该指针。我们可以将有用的信息嵌入到空闲的存储空间,但是这需要对系统甚至编译器进行全局更改。我们这里的设计使错误检查非常困难。
反例
我们当然可以和指针一起传入一个元素数量:
// 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()
}
相比于仅仅传递指针并且依赖于一些(未说明的)习惯来了解或者发现元素的数量,传入元素数量参数是更好(也更常见)的做法。然而(像代码里那样),一个简单的拼写错误会导致一系列严重的错误。f2()
的两个参数的关系是约定俗成的,而不是明确的。
此外,这样暗示了f2()
应该delete
他的参数(或者使调用者犯其他错误?)
反例
标准库资源管理指针指向对象时无法传递大小:
// 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
}
例子
我们需要将指针和元素数量作为一个整体对象传递:
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
}
这个设计将元素数量作为对象的一部分,这样不太可能出错而且动态(运行期)检测总是可行的,反之则不然。
例子
我们如何一起转让所有权和需要检验的信息?
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;
}
例子
- ???(PS:是说没有例子的意思吗?)
- (PS:这个好难懂,我猜测是表达想通过一个例子,“展示当传递多态基类的接口实际上知道他们需要的类型时,如何避免可能的类型检查?或字符串作为’自由风格’选项”,附上原文)
show possible checks are avoided by interfaces that pass polymorphic base classes around, when they actually know what they need? Or strings as “free-style” options
补充
- 标记(指针,计数)样式的接口(这将标记很多出于兼容性原因无法修复的示例)
- ???