231

After a lot of investigations with valgrind, I've made the conclusion that std::vector makes a copy of an object you want to push_back.

Is that really true ? A vector cannot keep a reference or a pointer of an object without a copy ?!

Thanks

jjxtra
  • 20,415
  • 16
  • 100
  • 140
benlaug
  • 2,691
  • 2
  • 17
  • 15
  • 31
    This is a basic principle of C++. Objects are values. Assignment makes a copy. Two variables referring to the same object is not possible unless you modify the type with `*` or `&` to make a pointer or reference. – Daniel Earwicker Feb 16 '10 at 18:04
  • 12
    @DanielEarwicker push_back actually does take a reference. It's not clear from the signature alone that it will make a copy. – Rag Apr 10 '14 at 07:55
  • 4
    @BrianGordon - Not saying it is! Hence the need for the guiding principle. Even so, we can deduce something from the signature of `push_back`: it takes a `const&`. Either it throws the value away (useless), or there's a retrieval method. So we look at the signature of `back`, and it returns plain `&`, so either the original value was copied or the `const` has been silently cast away (very bad: potentially undefined behaviour). So assuming the designers of `vector` were rational (`vector` not withstanding) we conclude it makes copies. – Daniel Earwicker Apr 10 '14 at 09:49

8 Answers8

238

Yes, std::vector<T>::push_back() creates a copy of the argument and stores it in the vector. If you want to store pointers to objects in your vector, create a std::vector<whatever*> instead of std::vector<whatever>.

However, you need to make sure that the objects referenced by the pointers remain valid while the vector holds a reference to them (smart pointers utilizing the RAII idiom solve the problem).

Alexander Gessler
  • 45,603
  • 7
  • 82
  • 122
  • 1
    I would also note that, if you use raw pointers, you are now responsible for cleaning up after them. There's no good reason to do this (not one I can think of anyway), you should always use a smart pointer. – Ed S. Nov 02 '12 at 00:03
  • 1
    that said, you should not use std::auto_ptr in stl containers, for more info : [why-is-it-wrong-to-use-stdauto-ptr-with-standard-containers](http://stackoverflow.com/questions/111478/why-is-it-wrong-to-use-stdauto-ptr-with-standard-containers) – OriginalCliche Apr 11 '13 at 18:48
  • 42
    Since C++11, `push_back` will perform a move instead of a copy if the argument is an rvalue reference. (Objects can be converted to rvalue references with `std::move()`.) – Emil Laine Aug 07 '15 at 17:12
  • 2
    @tuple_cat your comment should say "if the argument is an rvalue". (If the argument is the name of an entity declared as rvalue reference, then the argument is actually an lvalue and will not be moved from) - check my edit to the answer of "Karl Nicoll" which made that mistake initially – M.M Apr 16 '17 at 00:17
  • There is an [answer](https://stackoverflow.com/a/40358499/1906174) below but to make it clear: Since C++11 also use `emplace_back` to avoid any copy or move (construct object in place provided by the container). – Wormer Jul 31 '18 at 13:39
  • @emlai c++ newbie, when I check the doc for vector::push_back, I saw the signature takes a reference `void push_back( const T& value )` but actually it is still taking a copy? This is really confusing – torez233 Dec 03 '22 at 19:34
  • @torez233 Yeah that signature has to make a copy because std::vector has to store the value inside the vector, not just a reference to it. The copy is made inside the function. The new overload that takes an rvalue reference, `void push_back( T&& value )`, can avoid making the copy (by moving the parameter). – Emil Laine Dec 04 '22 at 21:11
66

From C++11 onwards, all the standard containers (std::vector, std::map, etc) support move semantics, meaning that you can now pass rvalues to standard containers and avoid a copy:

// 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");

Alternatively you can use various smart pointers to get mostly the same effect:

std::unique_ptr example

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 example

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.
Karl Nicoll
  • 16,090
  • 3
  • 51
  • 65
  • 2
    Note that `std::make_unique` is (annoyingly) available only in C++14 and above. Make sure you tell your compiler to set its standard conformance accordingly if you want to compile these examples. – András Aszódi Apr 07 '17 at 13:45
  • In 5a you can use `auto pFoo =` to avoid repetition; and all of the `std::string` casts can be removed (there is implicit conversion from string literals to `std::string`) – M.M Apr 16 '17 at 00:12
  • 2
    @user465139 `make_unique` can easily be implemented in C++11, so it's only a slight annoyance for someone stuck with a C++11 compiler – M.M Apr 16 '17 at 00:14
  • 1
    @M.M : Indeed. Here is textbook implementation: `template unique_ptr make_unique(Args&&... args) { return unique_ptr{new T{args...}}; }` – András Aszódi Apr 19 '17 at 10:29
  • @M.M - Good points for `std::string` and `auto` usage. I've updated my answer. – Karl Nicoll Apr 19 '17 at 10:46
  • If I `emplace_back` a `std::shared_ptr` into a `std::vector`, do the raw pointer before and after the emplace point to the same memory location? – Anakin Dec 09 '19 at 17:01
  • 1
    @Anakin - Yes they should do, but only if you copy. If you use `std::move()` with `std::shared_ptr`, the original shared pointer might have it's pointer changed since ownership was passed to the vector. See here: http://coliru.stacked-crooked.com/a/99d4f04f05e5c7f3 – Karl Nicoll Dec 10 '19 at 10:18
  • Another question if you can help me. Consider I create `std::shared_ptr`of several Derived classes and `emplace_back` them into a `std::vector` that holds `std::shared_ptr`s of the Base; do I have same underlying memory? Or does it change due to casting? – Anakin Dec 10 '19 at 10:24
  • @Anakin - Yep, you can do that. You can pass the `std::shared_ptr` to the vector holding `std::shared_ptr` and the pointer would remain the same :). – Karl Nicoll Dec 10 '19 at 10:41
  • I tried to demonstrate the omitting copy constructor based on move semantic - but it does not work as described - copy Ctor still called, demo is here: https://onlinegdb.com/b_julh3Kn – N0dGrand87 Jun 22 '21 at 22:40
  • vector stores objects in heap. if you create an object in stack and puch_back, the object should be copied to heap. So, in example 2 It probably still will be expensive mem copy operation under the hood. – x4444 Jun 20 '23 at 01:00
38

Yes, std::vector stores copies. How should vector know what the expected life-times of your objects are?

If you want to transfer or share ownership of the objects use pointers, possibly smart pointers like shared_ptr (found in Boost or TR1) to ease resource management.

Georg Fritzsche
  • 97,545
  • 26
  • 194
  • 236
  • 4
    learn to use shared_ptr - they do exactly what you want. My favorite idiom is typedef boost::shared_ptr FooPtr; Then make containers of FooPtrs – pm100 Feb 16 '10 at 18:00
  • 3
    @pm100 - Do you know `boost::ptr_vector` ? – Manuel Feb 16 '10 at 18:01
  • According to http://www.boost.org/doc/libs/1_42_0/libs/ptr_container/doc/ptr_vector.html, it's a raw pointer, not a smart one. Dangerous. – Steven Sudit Feb 16 '10 at 18:11
  • 2
    I also like to use `class Foo { typedef boost::shared_ptr ptr; };` to just write `Foo::ptr`. – Mathias Soeken Feb 16 '10 at 18:39
  • @manuel - no i do not - i will look. @rupert nice style but it has one weakness, I must own the class – pm100 Feb 16 '10 at 18:57
  • @Steven. Not dangerous at all. ptr_vector holds the ownership of the object. – Nemanja Trifunovic Feb 16 '10 at 19:02
  • ptr_vector seems to have some corner case advantages but is non shared - too dangerous compared to the 'fire and forget' behavior of shared_ptrs – pm100 Feb 16 '10 at 19:03
  • @Rupert Jones, pm100 - I've been using `ptr_vector` for quite a while and I've never felt the need for shared ownership – Manuel Feb 16 '10 at 19:21
  • @Nemanja: You're right. The description claims it's a raw pointer, but the header clearly shows auto_ptr is being used. Bad documentation! – Steven Sudit Feb 16 '10 at 19:53
  • 2
    @pm100 - `shared_ptr` is not exactly fire and forget. See http://stackoverflow.com/questions/327573/ and http://stackoverflow.com/questions/701456/ – Daniel Earwicker Feb 16 '10 at 20:00
  • 2
    shared_ptr is good if you have shared ownership, but it is generally overused. unique_ptr or boost scoped_ptr make much more sense when the ownership is clear. – Nemanja Trifunovic Feb 16 '10 at 20:10
  • 1
    "scoped_ptr cannot be used in C++ Standard Library containers. Use shared_ptr if you need a smart pointer that can." http://www.boost.org/doc/libs/1_41_0/libs/smart_ptr/scoped_ptr.htm – Steven Sudit Feb 17 '10 at 17:11
19

std::vector always makes a copy of whatever is being stored in the vector.

If you are keeping a vector of pointers, then it will make a copy of the pointer, but not the instance being to which the pointer is pointing. If you are dealing with large objects, you can (and probably should) always use a vector of pointers. Often, using a vector of smart pointers of an appropriate type is good for safety purposes, since handling object lifetime and memory management can be tricky otherwise.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • 4
    it doesnt depend on the type. It always makes a copy. If its a pointer is makes a copy of the pointer – pm100 Feb 16 '10 at 17:59
  • 1
    You're both right. Technically, yes, it always makes a copy. Practically, if you pass it a pointer to the object, it copies the pointer, not the object. Safely, you should use an appropriate smart pointer. – Steven Sudit Feb 16 '10 at 18:00
  • 2
    Yes, it's always copying - However, the "object" the OP is referring to is most likely a class or struct, so I was referring to whether it's copying the "Object" depends on the definition. Poorly worded, though. – Reed Copsey Feb 16 '10 at 18:01
3

Not only does std::vector make a copy of whatever you're pushing back, but the definition of the collection states that it will do so, and that you may not use objects without the correct copy semantics within a vector. So, for example, you do not use auto_ptr in a vector.

Liz Albin
  • 1,479
  • 8
  • 8
1

if you want not the copies; then the best way is to use a pointer vector(or another structure that serves for the same goal). if you want the copies; use directly push_back(). you dont have any other choice.

rahmivolkan
  • 407
  • 2
  • 9
  • 1
    A note about pointer vectors: vector > is alot safer than vector and shared_ptr is part of the standard as of last year. – rich.e Mar 27 '12 at 07:51
0

Relevant in C++11 is the emplace family of member functions, which allow you to transfer ownership of objects by moving them into containers.

The idiom of usage would look like

std::vector<Object> objs;

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

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

The move for the lvalue object is important as otherwise it would be forwarded as a reference or const reference and the move constructor would not be called.

LemonPi
  • 1,026
  • 9
  • 22
  • 4
    `emplace_back` shouldn't be used with an existing object, but to construct a new one in place – MatG Jul 09 '21 at 09:23
-3

Why did it take a lot of valgrind investigation to find this out! Just prove it to yourself with some simple code e.g.

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

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

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

If "hello world" is printed, the object must have been copied

NexusSquared
  • 157
  • 1
  • 7
  • 6
    This doesn't constitute a proof. If the object wasn't copied, your last statement would be undefined behavior and _could_ print hello. – Mat Feb 07 '15 at 11:49
  • 7
    the correct test would be modifying one of the two after the insertion. If they were the same object (if the vector stored a reference), both would be modified. – Francesco Dondi Feb 12 '16 at 11:49
  • 1
    Showing a different pointer constitutes proof enough. In your example &vec[0] != &obj. Thereof obj was copied over vec[0]. – rxantos Apr 26 '21 at 20:33