С++ 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);
}
}