简体   繁体   English

在智能指针容器中添加项目

[英]Add an item in a container of smart pointers

Several ways to add an item in a container of smart pointers. 在智能指针容器中添加项目的几种方法。 I am wondering which way you will go for. 我想知道你会选择哪种方式。

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>

};

I personally go for Way 1. Any more advantages for Way 2 and 3 or disadvantages of Way 1 that I should go for 2 or 3? 我个人选择的方式1.方式2和方式3的任何更多优点或方式1的缺点,我应该去2或3?

sftrabbit gives many good points. sftrabbit给出了很多好处。 In the following common case. 在以下常见情况中。 How to use Way 2 or 3 to do it easily? 如何使用方式2或3轻松完成? User uses a dialog to generate a new derived item. 用户使用对话框生成新的派生项。 It is put on std::unique_ptr<DerivedItem> item . 它被放在std::unique_ptr<DerivedItem> item When click 'OK' button, it is added to the container. 单击“确定”按钮时,会将其添加到容器中。 If failed to add, go back to the dialog for an edit. 如果添加失败,请返回对话框进行编辑。

I vote for: 我投票支持:

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

Reasons: 原因:

  1. It's clear from the function signature that the client needs to pass ownership of the object to the MyContainer . 从函数签名中可以清楚地看出,客户端需要将对象的所有权传递给MyContainer If you choose option 1 instead, it's still not clear if the client should delete the object themselves or not, or even if they should be passing a dynamically allocated object. 如果您选择选项1,则仍然不清楚客户端是否应该自己delete对象,或者即使它们应该传递动态分配的对象。

  2. Client is forced to explicitly transfer ownership with std::move if they already have the object managed by a named std::unique_ptr . 如果客户端已经拥有由命名为std::unique_ptr管理的对象,则必须使用std::move显式转移所有权。 They won't accidentally lose ownership. 他们不会意外失去所有权。 Option 3 does not clearly express that it's going to take ownership. 选项3没有明确表示它将取得所有权。

  3. When we have std::make_unique (N3588) , the method for adding an element will be: 当我们有std::make_unique (N3588)时 ,添加元素的方法将是:

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

    This avoids using new and improves exception safety in some situations. 这避免了在某些情况下使用new并改善了异常安全性。

  4. The issue you've given for derived objects is not really a problem. 您为派生对象提供的问题并不是真正的问题。 You'll get a compile-time error if you do it incorrectly. 如果你做错了,你会得到一个编译时错误。

  5. If the interface changes to use a different type of smart pointer, the client will want to know. 如果界面更改为使用不同类型的智能指针,则客户端将需要知道。 They don't want to continue passing objects thinking that they're passing ownership if in reality they're sharing it. 他们不想继续传递认为他们通过所有权的对象,如果实际上他们正在共享它。 They'll especially want to know if the opposite happens. 他们特别想知道相反的情况。

Unfortunately the first way seriously compromises type safety – you've pointed that our yourself in the disadvantages. 不幸的是,第一种方式严重危及类型安全 - 你已经指出我们自己处于劣势。 I think those concerns are overriding any advantages this method may have. 我认为这些担忧超越了这种方法可能带来的任何好处。

In particular, the potential error with the second method when using a derived object is caught at compile time so it's annoying, but safe ! 特别是,在编译时捕获使用派生对象时第二种方法的潜在错误,因此它很烦人,但是很安全

I agree about your assessment that this usage leaks implementation details but in my experience such leakage is unavoidable – I agree with sfrabbit that this is actually a detail that the user of the class needs to know about. 我同意你的评估,这种用法泄露了实现细节,但根据我的经验,这种泄漏是不可避免的 - 我同意sfrabbit这实际上是这个类的用户需要了解的细节。

Consider this as another tool in the toolbox: 将此视为工具箱中的另一个工具:

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

This combines the advantages of Way 2 and Way 3. Ie it will only accept rvalue unique_ptr s (like 2), but if there is some failure in adding it to the container, it can retain ownership like 3. Could be used something like: 这结合了方式2和方式3的优点。即它只接受rvalue unique_ptr (如2),但如果将其添加到容器中有一些失败,它可以保留所有权,如3.可以使用类似于:

void
foo(MyContainer& c)
{
    std::unique_ptr<Item> p = get_Item();
    try
    {
        c.Add(std::move(p));
    }
    catch (...)
    {
        log_error_wtih(*p);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM