2013-03-21 2 views
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. При нажатии кнопки «ОК» он добавляется в контейнер. Если не удалось добавить, вернитесь в диалоговое окно для редактирования.

ответ

6

Я голосую за:

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. Если интерфейс изменился, чтобы использовать другой тип интеллектуального указателя, клиент будет хочет знать. Они не хотят продолжать передавать объекты, думая, что они передают владение, если на самом деле они делят его. Они особенно хотят знать, происходит ли противоположное.

+0

+1, особенно причина 2. – us2012

+0

Рассмотрим случай. Пользователь использует диалог для создания нового производного элемента. Он помещается на 'std :: unique_ptr item'. При нажатии кнопки «ОК» он добавляется в контейнер. Если не удалось добавить, вернитесь в диалоговое окно для редактирования. Какой способ удобнее? Благодарю. – user1899020

+0

@ user1899020 Я придерживаюсь своего ответа. Это не зависит от того, для чего именно вы используете «MyContainer». Я просто рекомендую разрешить распространение исключения, если вы не можете добавить «Элемент». Это зависит от клиента. –

2

К сожалению, первый способ серьезно поставил под угрозу безопасность типов - вы указали, что сами себя в недостатках. Я думаю, что эти опасения переоценивают любые преимущества, которые может иметь этот метод.

В частности, потенциальная ошибка со вторым методом при использовании производного объекта ломается во время компиляции, так что это раздражает, но safe!

Я согласен с вашей оценкой, что это использование затрудняет детали реализации, но по моему опыту такая утечка неизбежна - я согласен с sfrabbit в том, что на самом деле это деталь, о которой должен знать пользователь класса.

1

Рассмотрим это как еще один инструмент в панели инструментов:

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

Это сочетает в себе преимущества Пути 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); 
    } 
} 
+0

Это то же самое, что и путь 2? Может ли это сделать: 'std :: unique_ptr a (новый элемент); Добавить (a); 'как путь 3? – user1899020

Смежные вопросы