【问题标题】:null checks not required for const unique_ptr returned from factory method?从工厂方法返回的 const unique_ptr 不需要空检查?
【发布时间】:2019-04-05 10:46:28
【问题描述】:

我们有很多使用原始指针的现有代码,这些代码几乎在每次使用时都充满了空检查。

在尝试更简洁地编写更新的代码时,我们尝试使用工厂构造方法和 unique_ptrs。

我的问题是,在下面的代码中,一旦我们获得了工厂创建的对象 - sensorX - 我们是否可以在其余代码中使用它而无需对其进行进一步的空检查,因为它是一个 const unique_ptr?

DeviceFactory.h

class DeviceFactory
{
public:

    template<typename T>
    static unique_ptr<T> create(int id, std::string status)
    {
        auto device = unique_ptr<T>{ new T{ id, status } };

        if (!device) throw DeviceCreationException("device couldn't be created");

        return device;
    }
};

用法

const auto sensorX = DeviceFactory::create<Sensor>(123, "sensorX");

【问题讨论】:

  • 使用std::make_unique
  • 然后写你自己的your_cool_namespace::make_unique。您的功能与make_unique 相同(只有if (!device) 是多余的,它不能为空,导致new 抛出)。无需重新发明轮子。 How to implement make-unique in C++11.
  • (a) OP 不能使用make_unique 所以他们自己写。 (b) 贡献者谴责 OP 没有使用 make_unique。 (c) OP 指出他们不能使用make_unique。 (d) 贡献者指示 OP 自己编写。 (你无法弥补!)
  • 如果你觉得你的代码有比它应该有的更多的空检查,这里是 Herb Sutter 关于指针、智能指针、引用和参数传递的一些指导。 herbsutter.com/2013/06/05/…
  • @Eljay,感谢 Herb Sutter 链接 - 我们显然会将这些要点用于相关场景。

标签: c++ c++11 unique-ptr


【解决方案1】:

看起来您使用不太灵活的 API 编写了自己的 std::make_unique 版本。我建议调整 API,因为它会使升级更容易。

也就是说,您的问题是关于空值检查的。正如在 cmets 中多次指出的那样,您不需要在获取指针后进行检查,因为应该抛出 std::bad_alloc。但是,让我们假设您进行了另一个引发此类自定义异常的检查,而不是主要问题:您的 API 是什么,包括前置条件和后置条件。

在我们工作的代码库中,除非有文档说明,否则 std::unique_ptr 不允许为 nullptr。在这种情况下:不需要空检查。您可以将其记录为 nullptr,或者返回一个可选项,这可能表示无效状态。

我的习惯是在使用前或创建后对指针进行断言。但是,您可以使用 Clang/Gcc 的 sanitizers 自动执行该工作。 GSL 还提供了可用于此 nullptr 用法的检查。

我的建议:在创建变量后断言(或使用消毒剂),如果后置条件发生变化,应该在测试期间发现错误。与您的大学一起,您应该同意 unique_ptr 不应包含 null 除非记录了 nullptr 代表什么。

【讨论】:

  • 了解这是 make_unique 的较差版本,我们不知道。 “在使用前或创建后断言指针”-您是指在创建后仅一次,假设如果为 const,则不能将其设置为 nullptr?或者你的意思是在每次使用之前,比如福勒的保护条款?
【解决方案2】:

首先是测试:

if (!device) throw DeviceCreationException("device couldn't be created");

没有多大意义,因为在达到那个 if 子句时,device 不可能是 nullptr(至少对于给定的代码来说不是)

从用户的角度,已知该函数由于签名而返回一个unique_ptr,但是您需要阅读文档,如果DeviceFactory::create&lt;Sensor&gt;调用保证它不持有nullptr。即使文档可以保证这一点,您自己进行测试可能仍然是一个好主意,因为您不知道将来会一直如此。

所以问题是如果你总是将它保存为const unique_ptr 那么你为什么需要一个指针,而不是写:

template<typename T>
static T create(int id, std::string status)
{
    auto device = T{ id, status };

    return device;
}

但回到你的问题。如果const auto sensorX = ... 用不包含nullptrunique_ptr 进行初始化,那么可以保证它不会是nullptr 在其生命周期内。

【讨论】:

  • “所以问题是如果你总是将它保存为 const unique_ptr 那么你为什么需要一个指针,而不是写:”看起来我并没有真正清楚地问我的问题,但是您的回答@t.niese 非常适合我们的大多数场景。知道 Device 在创建时是非 null 和 const 是我们正在寻找的。​​span>
  • 好问题:在这种情况下,按价值行事。但是,当您拥有纯虚方法或无法移动时(在 c++17 之前?),这并不总是可能的。
【解决方案3】:

即使它不是 const unique_ptr,也可以这样做,前提是您从未将其重置为 null。

关于你的工厂函数,

        auto device = unique_ptr<T>{ new T{ id, status } };
        if (!device) throw DeviceCreationException("device couldn't be created");

第二行的检查应该被删除,因为简单的 new 永远不会返回 null(好吧,除非你对实现默认值做了一些非常糟糕的事情,我假设)。

【讨论】:

  • 您是在说“如果您从未将其设置为 null,则永远不需要检查它是否为 null”?
【解决方案4】:

unique_ptr 仍然可以是nullptr,只是表示这个对象处理了底层资源,而不是有获取的资源。

所以你仍然需要像以前一样检查对象是否存在。

旁注:什么禁止某人致电您的工厂而不将结果存储在unique_ptr 中?只要允许,您就必须检查。

【讨论】:

  • "unique_ptr 仍然可以是 nullptr" - 可以在我上面的示例中,我已经完成了“if (!device)”检查,并且创建的对象是 const 吗?
  • 您将这个 unique_ptr 作为 const& 到处传递?
  • 是的,我们希望尽可能添加 const,它首先被声明为 const。我们的工具集包括 Klocwork 和 Resharper C++,它们确定了我们可以添加 const 的区域。
【解决方案5】:

一旦我们获得了工厂创建的对象 - sensorX - 我们是否可以在其余代码中使用它而不对其进行进一步的空检查?

正如其他答案所表明的那样,答案是“否”。但是您可以做的是使用指南中的owner&lt;&gt; 模板(MS implementation)、described

  • 您已经建立了一个指向工厂创建对象的(原始)指针是非空的,请将其包装在 owner&lt;T*&gt; 中;
  • 让所有权获取方法将 owner&lt;T*&gt; 而不是 T* 作为参数。

这将保证,静态,您在拨打电话之前已确保拥有所有权 - 因此您无需检查

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-23
    相关资源
    最近更新 更多