Это основной принцип C ++. Объекты являются ценностями. Назначение делает копию. Две переменные, ссылающиеся на один и тот же объект, невозможны, если вы не измените тип с помощью *или не &создадите указатель или ссылку.
Даниэль Уорвикер
8
@DanielEarwicker push_back на самом деле берет ссылку. Из подписи не ясно, что она сделает копию.
Брайан Гордон
3
@BrianGordon - Не говорю, что это так! Отсюда необходимость руководящего принципа. Тем не менее, мы можем вывести что-то из подписи push_back: это требует const&. Либо он выбрасывает значение (бесполезно), либо есть метод поиска. Итак, мы смотрим на сигнатуру back, и она возвращает обычное значение &, поэтому либо было скопировано исходное значение, либо constоно было тихо отброшено (очень плохо: потенциально неопределенное поведение). Таким образом, предполагая, что дизайнеры vectorбыли рациональными ( vector<bool>не выдерживающими), мы заключаем, что он делает копии.
Даниэль Эрвикер
Ответы:
183
Да, std::vector<T>::push_back()создает копию аргумента и сохраняет ее в векторе. Если вы хотите хранить указатели на объекты в вашем векторе, создайте std::vector<whatever*>вместо std::vector<whatever>.
Однако вам необходимо убедиться, что объекты, на которые ссылаются указатели, остаются действительными, пока вектор содержит ссылку на них (умные указатели, использующие идиому RAII, решают проблему).
Я также хотел бы отметить, что, если вы используете сырые указатели, теперь вы несете ответственность за их очистку. Нет веских причин для этого (я не могу придумать), вы всегда должны использовать умный указатель.
Начиная с C ++ 11, push_backбудет выполнять перемещение вместо копирования, если аргумент является ссылкой на значение. (Объекты могут быть преобразованы в rvalue ссылки с std::move().)
Emlai
2
@tuple_cat ваш комментарий должен сказать "если аргумент является rvalue". (Если аргумент - это имя сущности, объявленной как ссылка на rvalue, то аргумент на самом деле является lvalue и не будет перемещен из) - проверьте мою правку на ответ «Карла Николла», который изначально допустил эту ошибку
MM
Существует ответ ниже , но чтобы сделать это ясно: Так как C ++ 11 также использовать , emplace_backчтобы избежать копирования или перемещения (объект конструктов на месте предоставленный контейнер).
Wormer
34
Да, std::vectorхранит копии. Как vectorузнать, какова ожидаемая продолжительность жизни ваших объектов?
Если вы хотите передать или передать право собственности на объекты, используйте указатели, возможно, умные указатели, такие как shared_ptr(в Boost или TR1 ), чтобы упростить управление ресурсами.
научитесь использовать shared_ptr - они делают именно то, что вы хотите. Моя любимая идиома это typedef boost :: shared_ptr <Foo> FooPtr; Затем сделайте контейнеры из FooPtrs
pm100
3
@ pm100 - Ты знаешь boost::ptr_vector?
Мануэль
2
Я также хотел бы использовать, class Foo { typedef boost::shared_ptr<Foo> ptr; };чтобы просто написать Foo::ptr.
shared_ptr хорош, если у вас есть совместное владение, но обычно он чрезмерно используется. unique_ptr или boost scoped_ptr имеют гораздо больше смысла, когда право собственности ясно.
Неманя Трифунович
28
Начиная с C ++ 11 все стандартные контейнеры ( std::vectorи std::mapт. Д.) Поддерживают семантику перемещения, что означает, что теперь вы можете передавать значения в стандартные контейнеры и избегать копирования:
// Example object class.classobject{private:int m_val1;
std::string m_val2;public:// Constructor for object class.object(int val1, std::string&&val2):
m_val1(val1),
m_val2(std::move(val2)){}};
std::vector<object> myList;// #1 Copy into the vector.object foo1(1,"foo");
myList.push_back(foo1);// #2 Move into the vector (no copy).object foo2(1024,"bar");
myList.push_back(std::move(foo2));// #3 Move temporary into vector (no copy).
myList.push_back(object(453,"baz"));// #4 Create instance of object directly inside the vector (no copy, no move).
myList.emplace_back(453,"qux");
В качестве альтернативы вы можете использовать различные умные указатели, чтобы получить в основном один и тот же эффект:
std::unique_ptr пример
std::vector<std::unique_ptr<object>> myPtrList;// #5a unique_ptr can only ever be moved.auto pFoo = std::make_unique<object>(1,"foo");
myPtrList.push_back(std::move(pFoo));// #5b unique_ptr can only ever be moved.
myPtrList.push_back(std::make_unique<object>(1,"foo"));
std::shared_ptr пример
std::vector<std::shared_ptr<object>> objectPtrList2;// #6 shared_ptr can be used to retain a copy of the pointer and update both the vector// value and the local copy simultaneously.auto pFooShared = std::make_shared<object>(1,"foo");
objectPtrList2.push_back(pFooShared);// Pointer to object stored in the vector, but pFooShared is still valid.
Обратите внимание, что std::make_unique(досадно) доступно только в C ++ 14 и выше. Если вы хотите скомпилировать эти примеры, убедитесь, что вы указали компилятору установить его стандартное соответствие соответствующим образом.
Ларикс Децидуа
В 5а вы можете использовать, auto pFoo =чтобы избежать повторения; и все std::stringприведения могут быть удалены (есть неявное преобразование из строковых литералов в std::string)
MM
2
@ user465139 make_uniqueможет быть легко реализован в C ++ 11, так что это лишь небольшое раздражение для кого-то, кто застрял с компилятором C ++ 11
@ Анакин - Да, они должны делать, но только если вы копируете. Если вы используете std::move()с std::shared_ptr, исходный общий указатель может изменить свой указатель, так как владение было передано вектору. Смотрите здесь: coliru.stacked-crooked.com/a/99d4f04f05e5c7f3
Карл Николл
15
std :: vector всегда делает копию того, что хранится в векторе.
Если вы сохраняете вектор указателей, он создаст копию указателя, но не экземпляр, на который указывает указатель. Если вы имеете дело с большими объектами, вы можете (и, вероятно, должны) всегда использовать вектор указателей. Зачастую использование вектора интеллектуальных указателей соответствующего типа полезно в целях безопасности, поскольку в противном случае обработка времени жизни объекта и управление памятью могут быть сложными.
это не зависит от типа. Он всегда делает копию. Если его указатель делает копию указателя
pm100
Вы оба правы. Технически, да, он всегда делает копию. Практически, если вы передаете ему указатель на объект, он копирует указатель, а не объект. Безопасно, вы должны использовать соответствующий умный указатель.
Стивен Судит
1
Да, это всегда копирование - однако, «объект», на который ссылается OP, скорее всего, является классом или структурой, поэтому я имел в виду, зависит ли это от копирования «Объекта», от определения. Хотя плохо сформулировано.
Рид Копси,
3
Мало того, что std :: vector делает копию того, что вы отодвигаете назад, но определение коллекции гласит, что это будет сделано, и что вы не можете использовать объекты без правильной семантики копирования в векторе. Так, например, вы не используете auto_ptr в векторе.
В C ++ 11 релевантно emplaceсемейство функций-членов, которые позволяют передавать права собственности на объекты, перемещая их в контейнеры.
Форма использования будет выглядеть
std::vector<Object> objs;Object l_value_obj {/* initialize */};// use object here...
objs.emplace_back(std::move(l_value_obj));
Перемещение для объекта lvalue важно, так как в противном случае оно будет переадресовано как ссылка или константная ссылка, и конструктор перемещения не будет вызван.
если вы хотите не копии; тогда лучше всего использовать вектор-указатель (или другую структуру, которая служит для той же цели). если вы хотите копии; используйте непосредственно push_back (). у вас нет другого выбора.
Примечание о векторах указателей: vector <shared_ptr <obj>> намного безопаснее, чем vector <obj *>, а shared_ptr является частью стандарта с прошлого года.
rich.e
-1
Почему потребовалось много исследований Valgrind, чтобы выяснить это! Просто докажите это себе с помощью простого кода, например
Это не является доказательством. Если объект не был скопирован, ваш последний оператор будет неопределенным поведением и может напечатать привет.
Мат
4
правильный тест будет модифицировать один из двух после вставки. Если бы они были одним и тем же объектом (если вектор содержал ссылку), оба они были бы изменены.
*
или не&
создадите указатель или ссылку.push_back
: это требуетconst&
. Либо он выбрасывает значение (бесполезно), либо есть метод поиска. Итак, мы смотрим на сигнатуруback
, и она возвращает обычное значение&
, поэтому либо было скопировано исходное значение, либоconst
оно было тихо отброшено (очень плохо: потенциально неопределенное поведение). Таким образом, предполагая, что дизайнерыvector
были рациональными (vector<bool>
не выдерживающими), мы заключаем, что он делает копии.Ответы:
Да,
std::vector<T>::push_back()
создает копию аргумента и сохраняет ее в векторе. Если вы хотите хранить указатели на объекты в вашем векторе, создайтеstd::vector<whatever*>
вместоstd::vector<whatever>
.Однако вам необходимо убедиться, что объекты, на которые ссылаются указатели, остаются действительными, пока вектор содержит ссылку на них (умные указатели, использующие идиому RAII, решают проблему).
источник
push_back
будет выполнять перемещение вместо копирования, если аргумент является ссылкой на значение. (Объекты могут быть преобразованы в rvalue ссылки сstd::move()
.)emplace_back
чтобы избежать копирования или перемещения (объект конструктов на месте предоставленный контейнер).Да,
std::vector
хранит копии. Какvector
узнать, какова ожидаемая продолжительность жизни ваших объектов?Если вы хотите передать или передать право собственности на объекты, используйте указатели, возможно, умные указатели, такие как
shared_ptr
(в Boost или TR1 ), чтобы упростить управление ресурсами.источник
boost::ptr_vector
?class Foo { typedef boost::shared_ptr<Foo> ptr; };
чтобы просто написатьFoo::ptr
.shared_ptr
это точно не огонь и забудь. См. Stackoverflow.com/questions/327573 и stackoverflow.com/questions/701456Начиная с C ++ 11 все стандартные контейнеры (
std::vector
иstd::map
т. Д.) Поддерживают семантику перемещения, что означает, что теперь вы можете передавать значения в стандартные контейнеры и избегать копирования:В качестве альтернативы вы можете использовать различные умные указатели, чтобы получить в основном один и тот же эффект:
std::unique_ptr
примерstd::shared_ptr
примеристочник
std::make_unique
(досадно) доступно только в C ++ 14 и выше. Если вы хотите скомпилировать эти примеры, убедитесь, что вы указали компилятору установить его стандартное соответствие соответствующим образом.auto pFoo =
чтобы избежать повторения; и всеstd::string
приведения могут быть удалены (есть неявное преобразование из строковых литералов вstd::string
)make_unique
может быть легко реализован в C ++ 11, так что это лишь небольшое раздражение для кого-то, кто застрял с компилятором C ++ 11template<typename T, typename... Args> unique_ptr<T> make_unique(Args&&... args) { return unique_ptr<T>{new T{args...}}; }
std::move()
сstd::shared_ptr
, исходный общий указатель может изменить свой указатель, так как владение было передано вектору. Смотрите здесь: coliru.stacked-crooked.com/a/99d4f04f05e5c7f3std :: vector всегда делает копию того, что хранится в векторе.
Если вы сохраняете вектор указателей, он создаст копию указателя, но не экземпляр, на который указывает указатель. Если вы имеете дело с большими объектами, вы можете (и, вероятно, должны) всегда использовать вектор указателей. Зачастую использование вектора интеллектуальных указателей соответствующего типа полезно в целях безопасности, поскольку в противном случае обработка времени жизни объекта и управление памятью могут быть сложными.
источник
Мало того, что std :: vector делает копию того, что вы отодвигаете назад, но определение коллекции гласит, что это будет сделано, и что вы не можете использовать объекты без правильной семантики копирования в векторе. Так, например, вы не используете auto_ptr в векторе.
источник
В C ++ 11 релевантно
emplace
семейство функций-членов, которые позволяют передавать права собственности на объекты, перемещая их в контейнеры.Форма использования будет выглядеть
Перемещение для объекта lvalue важно, так как в противном случае оно будет переадресовано как ссылка или константная ссылка, и конструктор перемещения не будет вызван.
источник
если вы хотите не копии; тогда лучше всего использовать вектор-указатель (или другую структуру, которая служит для той же цели). если вы хотите копии; используйте непосредственно push_back (). у вас нет другого выбора.
источник
Почему потребовалось много исследований Valgrind, чтобы выяснить это! Просто докажите это себе с помощью простого кода, например
Если напечатан «hello world», объект должен быть скопирован
источник