可能的重复:
防御性编程

今天早上我们就防御性编程的主题进行了精彩的讨论。我们进行了代码审查,其中传入了一个指针,但没有检查它是否有效。

有些人认为只需要检查空指针。我质疑是否可以在更高的级别检查它,而不是它所传递的每个方法,并且如果该点另一端的对象不满足某些要求,则检查 null 是一种非常有限的检查。

我理解并同意检查 null 总比什么都不检查好,但在我看来,仅检查 null 会带来一种错误的安全感,因为它的范围有限。如果要确保指针可用,请检查是否有空值以上的内容。

您在这个主题上有什么经验?如何在代码中编写对传递给从属方法的参数的防御?

有帮助吗?

解决方案

在代码完成2中,在错误处理的章节中,我被介绍给路障的想法。从本质上说,路障是代码,严格验证所有输入进入它。路障内的代码可以假设任何无效的输入已经被处理了,而且这是收到的意见都很好。路障里面,代码只需要担心路障内传递给它的其他代码有关的无效数据。断言条件和审慎的单元测试可以增加搭成代码的信心。这样一来,你的程序更具有防御性的路障,但没有那么街垒里。想想它的另一种方式是,在路障,你总是正确处理错误,并路障里面你只是断言在调试版本的条件。

至于使用原始指针去,通常你能做的最好的断言,指针不为空。如果你知道什么应该是在内存中,然后你可以确保内容在某些方面是一致的。这就引出了为什么在一个对象,它可以验证它的一致性本身内存没有包裹起来的问题。

那么,你为什么要使用在这种情况下原始指针?它会更好使用引用或智能指针?是否指针包含数字数据,如果是这样,这将是更好的,其中管理该指针的生命周期的对象包起来?

回答这些问题可以帮助你找到一种方法,是更多的防守,因为你会设计更易于维护结束。

其他提示

是防御的最好方法是不检查指针运行时为空,但为避免使用指针可能为空开始与

如果该对象在必须被传递不为空,则使用参考!或按值传递吧!或使用某种形式的智能指针。

做防御性编程最好的方法是在编译时间赶上你的错误。 如果它被认为是一个错误的对象为空或指向垃圾,那么你应该做这些事情的编译错误。

最终,你没有如果指针指向知道该有效目标的方式。因此,而不是检查的一个的具体角落的情况下(这比真正危险的人很不常见,指着无效对象指针),使错误使用,保证有效性的数据类型是不可能的。

我想不出其他主流语言,它允许你在为C ++那样的编译时间捕捉尽可能多的错误。使用这种能力。

有没有办法检查一个指针是有效的。

在所有严重,这取决于你想多少虫子有后,你已经造成的。

检查空指针是肯定的东西,我会考虑必要但不充分。还有很多其他的固体原则,你可以使用开始与您的代码的入口点(例如,输入验证=确实该指针指向一些有用的东西)和退出点(例如,你以为指针指向一些有用的东西,但它发生在原因代码抛出异常)。

总之,如果你假设每个人都打电话给你的代码是要尽自己所能去毁掉你的生活,你可能会发现很多的罪魁祸首的。

EDIT为了清楚:一些其他的答案正在谈论的单元测试。我坚信,测试代码有时的更多的比,它的测试(这取决于谁的测量值)的代码是有价值的。这么说,我也觉得单元测试的的必要但不充分的防御性编码。

具体的例子:考虑在案,以返回匹配您的请求值的集合第三方搜索方法。不幸的是,是不是该方法的文档中明确的是,原开发商决定,这将是更好的返回空,而不是一个空的集合,如果没有符合你的要求。

所以,现在,你打电话给你的防守和良好单元测试方法的思想(也极为缺乏内部空指针检查)和热潮! NullPointerException异常,如果没有内部检查,你有没有处理的方式:

defensiveMethod(thirdPartySearch("Nothing matches me")); 
// You just passed a null to your own code.

我设计的“让它崩溃”学校的大风扇。 (免责声明:我没有医疗设备,航空电子设备,或核电相关的软件工作。)如果你的程序炸毁,你火起来的调试器,找出原因。相反,如果你的程序继续运行已检测到非法参数后,由它崩溃的时候,你可能不知道哪里出了问题。

好码由许多小的功能/方法,并添加参数检查到的代码那些片段的每一个的十几行使得它更难阅读和难以维护。保持简单。

我可能有点极端,但我不喜欢防御性编程,我认为是懒惰引入了原理。

对于这个特定的示例,断言指针不为空是没有意义的。如果您想要一个空指针,那么没有比使用引用更好的方法来实际强制执行它(并同时清楚地记录它)。而且它的文档实际上将由编译器强制执行,并且在运行时不会花费任何费用!

一般来说,我倾向于不直接使用“原始”类型。让我们举例说明:

void myFunction(std::string const& foo, std::string const& bar);

可能的值有哪些 foobar ?嗯,这几乎只受什么限制 std::string 可能含有...这是相当模糊的。

另一方面:

void myFunction(Foo const& foo, Bar const& bar);

好多了!

  • 如果人们错误地颠倒了参数的顺序,编译器会检测到
  • 每个类单独负责检查该值是否正确,用户不会有负担。

我倾向于支持强类型。如果我有一个仅由字母字符组成且最多 12 个字符的条目,我宁愿创建一个小类来包装 std::string, ,用一个简单的 validate 内部使用的方法来检查分配,并传递该类。这样我就知道,如果我测试验证例程一次,我实际上不必担心该值可以到达我的所有路径>当它到达我时它将被验证。

当然,这并不是说代码不应该被测试。只是我更喜欢强封装,在我看来,输入验证是知识封装的一部分。

因为没有任何规则可以无例外地出现......暴露的接口必然充斥着验证代码,因为你永远不知道会发生什么。然而,对于 BOM 中的自我验证对象,一般来说是相当透明的。

“单元测试验证代码,它应该做的事” - >“产品代码试图验证其没有做它不是什么应该做的。”

我甚至不会检查空自己,除非其公布的API的一部分。

有非常依赖;是有史以来代码外部调用到组问题的方法中,或者是它的内部方法?

有关内部方法,你可以测试足以让这一个有争议的问题,如果你正在构建的代码,其目的是尽可能高的性能,你可能不希望花时间检查输入你相当不错的肯定是正确的。

有关外部可见的方法 - 如果您有任何 - 你应该总是仔细检查您输入。总是

从调试的角度来看,这是最重要的,你的代码是快速失败的。代码失败越早,就越容易找到故障点。

有关内部方法,我们通常坚持断言为这些类型的检查。这并得到错误的单元测试拿起(你有很好的测试覆盖率,对吧?),或者至少在集成测试与上断言运行。

<强>检查空指针是<强>只有一半的故事的下, 你也应该指定一个空值到每一个未分配的指针。点击 最负责任的API也将这样做。结果 检查空指针进来CPU周期很便宜,有一个应用程序崩溃,一旦它的交付可以花费你和你的公司在金钱和名誉。

可以跳过空指针检查该代码是在一个专用接口必须的和完全的控制/或通过运行一个单元测试或一些调试生成测试检查空(例如断言)

在这个问题中,我想解决一些问题:

  1. 编码指南应指定您直接处理引用或值,而不是使用指针。根据定义,指针是仅在内存中保存地址的值类型——指针的有效性是特定于平台的,并且意味着很多事情(可寻址内存的范围、平台等)。
  2. 如果您发现自己出于任何原因需要指针(例如动态生成的多态对象),请考虑使用智能指针。智能指针通过“普通”指针的语义为您提供了许多优势。
  3. 例如,如果某个类型具有“无效”状态,则该类型本身应该为此提供支持。更具体地说,您可以实现 NullObject 模式,该模式指定“错误定义”或“未初始化”对象的行为方式(可能通过引发异常或提供无操作成员函数)。

您可以创建一个执行 NullObject 默认值的智能指针,如下所示:

template <class Type, class NullTypeDefault>
struct possibly_null_ptr {
  possibly_null_ptr() : p(new NullTypeDefault) {}
  possibly_null_ptr(Type* p_) : p(p_) {}
  Type * operator->() { return p.get(); }
  ~possibly_null_ptr() {}
  private:
    shared_ptr<Type> p;
    friend template<class T, class N> Type & operator*(possibly_null_ptr<T,N>&);
};

template <class Type, class NullTypeDefault>
Type & operator*(possibly_null_ptr<Type,NullTypeDefault> & p) {
  return *p.p;
}

然后使用 possibly_null_ptr<> 模板,如果您支持可能指向具有默认派生“空行为”的类型的空指针。这使得设计中明确表明“空对象”有可接受的行为,并且这使得您的防御实践记录在代码中 - 并且比一般指南或实践更具体。

如果你需要做的事情的指针

指针,才应使用。如指针运算以横截一些数据结构。然后,如果可能,应在一类被封装。

如果指针被传递到函数做它所指向的对象的东西,那么传递一个参考来代替。

对于防御性编程的一种方法是断言几乎一切,你可以。在项目开始时很是烦人,但后来它是一个很好的辅助单元测试。

若干答案地址如何在你的代码编写防御的问题,但没有多少是说过“你应该怎么防守呢?”。这是你必须根据你的软件组件的关键性评价的东西。

我们正在做飞行软件和软件误差范围的飞机/机组人员的损失的影响,从一个小麻烦。我们根据影响编码标准,测试等你需要评估如何你的软件使用和错误的影响,并设置你想要什么级别防御的(和可以负担得起),其潜在的负面影响归类不同的软件。该 DO-178B标准称此为 “设计保证等级”。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top