5

在智能指针容器中添加项目的几种方法。我想知道你会走哪条路。

class MyContainer
{
private:
    std::vector<std::unique_ptr<Item>> mItems;

public:
    bool Add(Item* item);
    // This is Way 1
    //
    // Advantages: 
    // - Easy to add derived items, such as Add(new DerivedItem);
    // - No interface change if smart pointer type changes to such as shared_ptr;
    //
    // Disadvantages:
    // - Don't explicitly show the item to add must be allocated on heap;
    // - If failed to add, user has to delete the item.

    bool Add(std::unique_ptr<Item> item);
    // This is Way 2
    // Disadvantages and advantages are reversed from Way 1.
    // Such as to add derived item, Add(std::unique_ptr<Item>(new DerivedItem));
    //                                                    |
    //                               easy to write DerivedItem here for an error

    bool Add(std::unique_ptr<Item>& item);
    // This is Way 3
    // Similar to Way 2, but when failed to add, item still exist if it is a 
    // reference of outer unique_ptr<Item>

};

我个人选择方式 1。方式 2 和 3 的优势或方式 1 的劣势我应该选择 2 或 3 吗?

sftrabbit 给出了许多优点。在以下常见情况下。如何使用方式 2 或 3 轻松完成?用户使用对话框生成新的派生项。它被戴上std::unique_ptr<DerivedItem> item。当单击“确定”按钮时,它被添加到容器中。如果添加失败,请返回对话框进行编辑。

4

3 回答 3

5

我投票赞成:

bool Add(std::unique_ptr<Item> item);

原因:

  1. 从函数签名中可以清楚地看出,客户端需要将对象的所有权传递给MyContainer. 如果您选择选项 1,则仍然不清楚客户端是否应该delete自己使用对象,或者即使他们应该传递动态分配的对象。

  2. std::move如果客户已经拥有由命名管理的对象,他们将被迫显式转移所有权std::unique_ptr。他们不会意外失去所有权。选项 3 没有明确表示它将获得所有权。

  3. 当我们有std::make_unique(N3588)时,添加元素的方法将是:

    container.Add(std::make_unique<Item>());
    

    这避免new了在某些情况下使用并提高了异常安全性。

  4. 您为派生对象给出的问题并不是真正的问题。如果你做错了,你会得到一个编译时错误。

  5. 如果接口更改为使用不同类型的智能指针,客户端会知道。他们不想继续传递对象,以为他们正在传递所有权,如果实际上他们正在共享它。他们会特别想知道是否会发生相反的情况。

于 2013-03-21T14:11:56.237 回答
2

不幸的是,第一种方法严重损害了类型安全——你已经指出我们自己处于劣势。我认为这些担忧压倒了这种方法可能具有的任何优势。

特别是,使用派生对象时第二种方法的潜在错误在编译时被捕获,因此很烦人,但安全

我同意你的评估,即这种用法会泄漏实现细节,但根据我的经验,这种泄漏是不可避免的——我同意 sfrabbit 的观点,这实际上是课程用户需要了解的细节。

于 2013-03-21T14:18:26.547 回答
1

将此视为工具箱中的另一个工具:

bool Add(std::unique_ptr<Item>&& item);

这结合了方式 2 和方式 3 的优点。即它只接受 rvalue unique_ptrs(如 2),但如果将其添加到容器中出现一些失败,它可以像 3 一样保留所有权。可以使用如下:

void
foo(MyContainer& c)
{
    std::unique_ptr<Item> p = get_Item();
    try
    {
        c.Add(std::move(p));
    }
    catch (...)
    {
        log_error_wtih(*p);
    }
}
于 2013-03-22T14:27:34.437 回答