С++ 11-членная функция возвращает вектор исходных указателей из вектора unique_ptr

Я начинаю использовать функции С++ 11, и мне нравится использовать интеллектуальные указатели только для собственных объектов. Вот мой класс:

class MyClass {
  public:
    vector<MyObject*> get_objs() const;

  private:
    vector<unique_ptr<MyObject>> m_objs;
};

Семантика заключается в том, что MyClass принадлежит серию MyObject, которые создаются с помощью make_unique(). get_objs() возвращает вектор исходных указателей, чтобы различные вызывающие объекты обновляли объекты. Поскольку эти вызывающие лица не владеют объектами, поэтому функция не возвращает vector<unique_ptr>.

Но это означает, что мне нужно реализовать get_objs() следующим образом:

vector<MyObjects*> MyClass::get_objs() const
{
  vector<MyObjects*> ret;
  for (auto obj : my_objs) {
    ret.push_back(obj->get());
  }
  return ret;
}

Моя забота get_objs() называется довольно часто, каждый раз, когда есть накладные расходы для создания этого необработанного вектора указателя.

Я могу здесь что-то сделать? Если для сохранения накладных расходов нет С++ 11 трюков, должен ли я просто использовать тип vector<MyObject*> для m_objs в первую очередь?

UPDATE 1

Решение Джонатана Вакели, использующее operator[], улучшает мой, так что вызывающий может напрямую обращаться к отдельному объекту.

Есть ли другое решение? Я не против перебирать все места, вызывающие get_objs(),, но нравится видеть, есть ли еще лучшее решение.

Другое примечание - я не могу использовать BOOST, просто какое-то ограничение, с которым мне нужно жить.

Ответ 1

class MyClass {
  public:
   std::vector<std::unique_ptr<MyObject>> const& get_objs() const {
     return m_objs;
   }

  private:
    std::vector<std::unique_ptr<MyObject>> m_objs;
};

a const std::unique_ptr<MyObject>& не может украсть собственность и не совпадает с std::unique_ptr<const MyObject>. A const std::vector<std::unique_ptr<MyObject>>& может предоставить только const доступ к своим данным.

Ответ 2

Для начала вы можете использовать ret.reserve(m_objs.size()) для предварительного размещения правильного количества элементов.

В качестве альтернативы, не возвращайте вектор для вызывающих абонентов для итерации напрямую, но вместо этого выставляйте векторный интерфейс:

class MyClass {
  public:
    struct iterator;
    iterator begin();
    iterator end();
    MyObject* operator[](size_t n) { return m_objs[n].get(); }

  private:
    vector<unique_ptr<MyObject>> m_objs;
};

Это позволяет вызывающим пользователям напрямую изменять объекты, а не получать контейнер указателей.

Ответ 3

Если вы можете использовать Boost, попробуйте косвенный_iterator (http://www.boost.org/doc/libs/1_55_0b1/libs/iterator/doc/indirect_iterator.html). Вам нужно определить итератор, начать и завершить в своем классе:

typedef boost::indirect_iterator<vector<unique_ptr<MyObject>::iterator> iterator;
iterator begin() { return make_indirect_iterator(m_objs.begin()); }

Затем ваш класс предоставляет итератор, значение которого является ссылкой (не указателем!) на MyObject. Вы можете напрямую и напрямую обращаться к элементам вектора.

Ответ 4

Для записи я думаю, что что-то вроде Джонатан Вакели отвечает - это путь. Но поскольку вы попросили больше возможностей, другой должен использовать shared_ptr вместо unique_ptr:

class MyClass {
  public:
    const vector<shared_ptr<MyObject>>& get_objs() const {
        return m_objs;
    }

  private:
    vector<shared_ptr<MyObject>> m_objs;
};

Это улучшает исходный код двумя способами:

  • Больше не нужно создавать новый вектор в get_objs; вы можете просто вернуть ссылку на тот, который у вас есть.
  • Вам больше не нужно беспокоиться о диких указателях в случае, когда вызывающий объект сохраняет возвращаемое значение дольше, чем возвращаемый объект - shared_ptr гарантирует, что объекты с указателем не удаляются, пока все ссылки не будут освобожден.

В другом примечании get_objs, возможно, не должно быть const. Код вызова не может изменить сам вектор, но он может изменить MyObject, который он содержит.

Ответ 5

Другой способ - не возвращать какие-либо объекты вообще и инкапсулировать, как вы храните данные (скажите, не спрашивайте). Обычно, когда вы получаете предметы, вы заканчиваете их повторением, поэтому имеет смысл обернуть это в классе и передать функцию, через которую вы хотите, чтобы ваши объекты проходили.

Невозбужденным способом будет что-то вроде

void MyClass::VisitItems(std::function<void, MyObject&> f)
{
    for (auto obj : my_objs)
    {
        f(*obj);
    }
}