Когда имеет смысл использовать unique_ptr с контейнерами STL?(C ++ 11) - PullRequest
6 голосов
/ 07 января 2012

Контейнер unique_ptr, кажется, не имеет особого смысла: вы не можете использовать его со списками инициализаторов, и мне не удалось перебрать контейнер (обходные пути ниже). Я что-то неправильно понимаю? Или когда имеет смысл использовать unique_ptr и контейнеры STL?

#include <memory>
#include <vector>

using namespace std;

struct Base { void go() { }  virtual ~Base() { } }; 
// virtual ~Base() = default; gives
// "declared virtual cannot be defaulted in the class body" why?

class Derived : public Base { };

int main() {

  //vector<unique_ptr<Base>> v1 = { new Derived, new Derived, new Derived };
  //vector<shared_ptr<Base>> v2 = { new Derived, new Derived, new Derived };
  vector<Base*> v3 = { new Derived, new Derived, new Derived };
  vector<shared_ptr<Base>> v4(v3.begin(), v3.end());
  vector<unique_ptr<Base>> v5(v3.begin(), v3.end());

  for (auto i : v5) { // works with v4
    i->go();
  }
  return 0;
}

<ч /> Следующие вопросы помогли мне найти эти обходные пути:

Ответы [ 3 ]

15 голосов
/ 07 января 2012
for (auto i : v5) {
  i->go();
}

Должно быть

for (auto& i : v5) { // note 'auto&'
  i->go();
}

В противном случае вы попытаетесь скопировать текущий элемент.

Кроме того, вы не можете использовать такой список инициализаторов, потому что конструкторыstd::unique_ptr и std::shared_ptr помечены explicit.Вам нужно сделать что-то вроде этого:

#include <iterator> // make_move_iterator, begin, end

template<class T>
std::unique_ptr<T> make_unique(){ // naive implementation
  return std::unique_ptr<T>(new T());
}

std::unique_ptr<Base> v1_init_arr[] = {
    make_unique<Derived>(), make_unique<Derived>(), make_unique<Derived>()
};

// these two are only for clarity
auto first = std::make_move_iterator(std::begin(v1_init_arr));
auto last = std::make_move_iterator(std::end(v1_init_arr));
std::vector<std::unique_ptr<Base>> v1(first, last);

std::vector<std::shared_ptr<Base>> v2 = {
    std::make_shared<Derived>(),
    std::make_shared<Derived>(),
    std::make_shared<Derived>()
};

И это Good Thing ™, потому что в противном случае вы можете потерять память (если выдает один из более поздних конструкторов, первые еще не связаны сумные указатели).Подсказка для unique_ptr необходима, поскольку списки инициализатора копируют свои аргументы, а поскольку unique_ptr s не копируются, вы получите проблему.


Тем не менее, яиспользуйте std::map<std::string, std::unique_ptr<LoaderBase>> для словаря загрузчиков в одном из моих проектов.

1 голос
/ 07 января 2012

На самом деле вы можете перебирать контейнер без проблем, используя std::unique_ptr<T> ... вам просто нужно получить доступ к ссылке (т. Е. Не к копии) уникального указателя, или вам действительно нужно использовать тип итератора сконтейнер.В вашем случае это будет что-то вроде vector<unique_ptr<Base>>::iterator или vector<unique_ptr<Base>>::const_iterator.

1 голос
/ 07 января 2012

unique_ptr имеет смысл в контейнерах STL, когда контейнер содержит объекты, которые не могут быть скопированы.Или если копировать их дорого или просто неправильно.

Вы получаете ту же функциональность, что и ваша

vector<Base*> v3 = { new Derived, new Derived, new Derived };

Но без утечек памяти, которые v3 приглашает.

...