Является ли std :: vector копированием объектов с помощью push_back? - PullRequest
149 голосов
/ 16 февраля 2010

После многих исследований с valgrind я пришел к выводу, что std :: vector делает копию объекта, который вы хотите push_back.

Это правда? Вектор не может хранить ссылку или указатель на объект без копии?!

Спасибо

Ответы [ 8 ]

162 голосов
/ 16 февраля 2010

Да, std::vector<T>::push_back() создает копию аргумента и сохраняет его в векторе. Если вы хотите хранить указатели на объекты в вашем векторе, создайте std::vector<whatever*> вместо std::vector<whatever>.

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

33 голосов
/ 16 февраля 2010

Да, std::vector хранит копии. Как vector знать, каково ожидаемое время жизни ваших объектов?

Если вы хотите передать или передать право собственности на объекты, используйте указатели, возможно, умные указатели, такие как shared_ptr (находится в Повышение или TR1 ) для облегчить управление ресурсами.

24 голосов
/ 01 ноября 2016

Начиная с C ++ 11, все стандартные контейнеры (std::vector, std::map и т. Д.) Поддерживают семантику перемещения, что означает, что теперь вы можете передавать значения в стандартные контейнеры и избегать копирования:

// Example object class.
class object
{
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.
15 голосов
/ 16 февраля 2010

std :: vector всегда делает копию того, что хранится в векторе.

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

3 голосов
/ 16 февраля 2010

Мало того, что std :: vector делает копию того, что вы отодвигаете назад, но определение коллекции гласит, что это будет сделано, и что вы не можете использовать объекты без правильной семантики копирования внутри вектора. Так, например, вы не используете auto_ptr в векторе.

2 голосов
/ 18 июля 2015

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

Форма использования будет выглядеть как

std::vector<Object> objs;

Object l_value_obj { /* initialize */ };
// use object here...

objs.emplace_back(std::move(l_value_obj));

Перемещение для объекта lvalue важно, так как в противном случае оно будет перенаправлено как ссылка или константная ссылка, а конструктор перемещения не будет вызван.

0 голосов
/ 07 февраля 2015

Почему для того, чтобы это выяснить, потребовалось много тщательного расследования! Просто докажите это себе с помощью простого кода, например,

std::vector<std::string> vec;

{
      std::string obj("hello world");
      vec.push_pack(obj);
}

std::cout << vec[0] << std::endl;  

Если напечатан «hello world», объект должен быть скопирован

0 голосов
/ 16 февраля 2010

если вы хотите не копии; тогда лучший способ - использовать вектор-указатель (или другую структуру, которая служит для той же цели). если вы хотите копии; используйте непосредственно push_back (). у вас нет другого выбора.

...