C++核心准则:编译时无法检查的内容应在运行时检查

1059 篇文章 278 订阅

原因

在程序中留下难以检测的错误会导致崩溃等不良结果

注意

理想情况下,我们会在编译时或执行时捕捉错误(不包括程序执行逻辑的错误)。

但是,我们不可能在编译器捕捉出所有的错误,通常也无法做到在执行时检出剩下的所有错误

但是,我们还是应该努力按照可检查的原则写代码,就像有充足的资源(分析程序,执行检查,机器资源、时间)那样。

反例

// f是分别编译的,而且有可能是动态加载的。
extern void f(int* p);

void g(int n)
{
    // 错误:元素的数量没有传递给 f()
    f(new int[n]);
}

在这里,关键信息(元素的数量)已被彻底“模糊”,以至于静态分析可能变得不可行,而动态检查在f()属于ABI的情况下可能非常困难,以至于我们无法“指示”该指针。我们可以将有用的信息嵌入到免费存储中,但这需要对系统甚至对编译器进行全局更改。我们这里的设计使错误检测变得非常困难。

注:ABI(Application Binary Interface)是操作系统为运行在该系统下的应用程序提供的二进制接口。。

反例

我们当然可以把元素的个数和指针一起传递过去:

// separately compiled, possibly dynamically loaded
extern void f2(int* p, int n);

void g2(int n)
{
    f2(new int[n], m);  // bad: 有可能将错误的元素数量传递给f()
}

相比只是传递一个指针然后通过某种(心照不宣)的转换以获取元素个数的方式,将元素个数作为参数传递会比较好(也更常见)。但是正如代码中显示的,一个简单的打字错误就可能导入严重的错误。函数f2()的两个参数之间的关系可以说约定俗成,但还是没有达到明确的程度。

另外,有一点比较含蓄,就是我们可以推断f2()会释放参数管理的内存(抑或这只是调用者的另一个错误?)

反例

当标准库中的资源管理指针指向一个对象时,如果传递大小信息会导致失败。

// f3s是分别编译的,有可能是动态加载的
// NB:假设调用代码是ABI兼容的,即使用兼容的C++编译器和相同的stdlib实现。
extern void f3(unique_ptr<int[]>, int n);

void g3(int n)
{
    f3(make_unique<int[]>(n), m);    // 不好: 分别传递所有权和大小
}

译者注:代码前的说明只是make_unique的补充说明,没有看出和本文的主题之间有什么关系。这段代码的问题应该在于同样的大小信息需要两次指定,这同样会带来出错的可能性。

示例

我们需要将指针和元素数量作为一个整体对象进行传递:

extern void f4(vector<int>&);   // separately compiled, possibly dynamically loaded
extern void f4(span<int>);      // separately compiled, possibly dynamically loaded
                                // NB:假设调用代码是ABI兼容的(即使用兼容的C++编译器和相同的stdlib实现)。

void g3(int n)
{
    vector<int> v(n);
    f4(v);                     // 传递参照,保留所有权
    f4(span<int>{v});          // 传递快照,保留所有权
}

这个设计将元素的个数作为对象的一部分,因此不大可能引发错误而且动态(执行时)检查总是可行的,虽然这样做的代价可能十分巨大。

示例

我们如何即传递所有权又传递用于检查的所有信息?

vector<int> f5(int n)    // OK: move
{
    vector<int> v(n);
    // ... initialize v ...
    return v;
}

unique_ptr<int[]> f6(int n)    // 不好:n 丢失
{
    auto p = make_unique<int[]>(n);
    // ... initialize *p ...
    return p;
}

owner<int*> f7(int n)    // 不好:n 丢失,而且有可能会忘记delete
{
    owner<int*> p = new int[n];
    // ... initialize *p ...
    return p;
}

译者注:第一段代码返回一个vector对象,它即包含指针又包含元素个数;第二段代码返回一个unique_ptr,所有权移交没有问题了,但是丢失了元素个数信息;第三段代码不但丢失了元素个数信息,而且没有办法保证内存一定会被删除。非常不幸,第三段代码正是我们日常开发中最常见的代码。

https://blog.csdn.net/sinat_18811413/article/details/108940235
https://blog.csdn.net/craftsman1970/article/details/103262847

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值