C++ vector push_back

2019-02-01 08:41发布

问题:

What is the proper way of pushing a new object element onto a std::vector? I want the data to be allocated in the vector. Will this copy the object newradio into the vector and then get rid of newradio when it goes out of scope (e.g. out of the stack)?

vector<Radio> m_radios;
Radio newradio(radioNum);
m_radios.push_back(newradio);

And then when I free the object containing m_radios, will this free all the memory allocated by the vector?

回答1:

std::vector manages its own memory. That means that, when the destructor of a vector is invoked the memory held by the vector is released. std::vector also invokes an object's destructor when it is removed (through erase, pop_back, clear or the vector's destructor).

When you do this:

Radio newradio(radioNum);
m_radios.push_back(newradio);

You add a copy of newradio (created using Radio's copy constructor) to the vector. newradio will be destroyed when it goes out of scope, and the copy will be destroyed when it is removed from the vector (as for any object).

That's an important point: std::vector only stores copies of an object, which means the object must have a meaningful copy constructor (and an assignment operator, but that's another issue). If you have a vector of pointers, then the pointer itself will be copied, and not what it points to. Note that this behavior is the same for every standard container (like std::list or std::set).

As a rule of thumb, if you're not using pointers, then you don't have to worry about releasing the memory yourself.

In C++11, most standard containers (including vector) have an emplace_back method that constructs an object in place at the end of the container. It takes a bunch of parameters and calls the constructor that best matches those parameters (or fails if no such constructor exist), using said constructor to create the object, without any copy, at the end of the container. So, the above code could be rewritten as:

m_radios.emplace_back(radioNum); // construct a Radio in place, 
                                 // passing radioNum as the constructor argument

Also in C++11, containers are usually move aware, so they no longer require objects to be copiable: if they are movable, then the container will move its contents as required (such as during reallocations, for instance). Copiable types are still required if you want to copy the vector.



回答2:

push_back() will store a copy of its argument within the vector. As long as Radio implements proper value semantics, there will be no problems with it.



回答3:

Yes pushing newRadio will push a copy of the Radio object into the vector. You also don't need to free the vector because it is local only so it will be destroyed once you are out of its scope. If for instance you wrote

vector<Radio> *m_radios = new vector<Radio>();

Then you would have to free that memory by calling the vector destructor manually.



回答4:

Try this:

#include<iostream.h>
#include<vector.h>

class base
{
int i;
public:

    base(int z=0){i=z;cout<<"okk constructor "<<i<<" called\n";}
    base(const base& b1){i=b1.i;cout<<"copy constructor "<<i<<" called\n";}
    void display(){cout<<" val is "<<i<<" \n";}
    ~base(){cout<<"destructor of "<<i<<" base called\n";}
};


    int main()
    {
        cout<<"before anything\n";
        vector<base> basev;
        base baseobj1(1);
        base baseobj2(2);
        base baseobj3(3);
        base baseobj4(4);
        base baseobj5(5);
        base baseobj6(6);
        base baseobj7(7);
        base baseobj8(8);
        base baseobj9(9);
        base baseobj10(10);


        basev.push_back(baseobj1);
        cout<<"second push back\n";
        basev.push_back(baseobj2);
        cout<<"third push back\n";
        basev.push_back(baseobj3);
        cout<<"fourth push back\n";
        basev.push_back(baseobj4);
        cout<<"fifth push back\n";
        basev.push_back(baseobj5);
        cout<<"sixth push back\n";
        basev.push_back(baseobj6);
        cout<<"seventh push back\n";
        basev.push_back(baseobj7);
        cout<<"eighth push back\n";
        basev.push_back(baseobj8);
        cout<<"ninth push back\n";
        basev.push_back(baseobj9);
        cout<<"10th push back\n";
        basev.push_back(baseobj10);
        cout<<"after all push back\n";


        cout<<"before clear\n";
        basev.clear();
        cout<<"after clear\n";


}

output:

before anything
okk constructor 1 called
okk constructor 2 called
okk constructor 3 called
okk constructor 4 called
okk constructor 5 called
okk constructor 6 called
okk constructor 7 called
okk constructor 8 called
okk constructor 9 called
okk constructor 10 called
copy constructor 1 called
second push back
copy constructor 1 called
copy constructor 2 called
destructor of 1 base called
third push back
copy constructor 1 called
copy constructor 2 called
copy constructor 3 called
destructor of 1 base called
destructor of 2 base called
fourth push back
copy constructor 4 called
fifth push back
copy constructor 1 called
copy constructor 2 called
copy constructor 3 called
copy constructor 4 called
copy constructor 5 called
destructor of 1 base called
destructor of 2 base called
destructor of 3 base called
destructor of 4 base called
sixth push back
copy constructor 6 called
seventh push back
copy constructor 7 called
eighth push back
copy constructor 8 called
ninth push back
copy constructor 1 called
copy constructor 2 called
copy constructor 3 called
copy constructor 4 called
copy constructor 5 called
copy constructor 6 called
copy constructor 7 called
copy constructor 8 called
copy constructor 9 called
destructor of 1 base called
destructor of 2 base called
destructor of 3 base called
destructor of 4 base called
destructor of 5 base called
destructor of 6 base called
destructor of 7 base called
destructor of 8 base called
10th push back
copy constructor 10 called
after all push back
before clear
destructor of 1 base called
destructor of 2 base called
destructor of 3 base called
destructor of 4 base called
destructor of 5 base called
destructor of 6 base called
destructor of 7 base called
destructor of 8 base called
destructor of 9 base called
destructor of 10 base called
after clear
destructor of 10 base called
destructor of 9 base called
destructor of 8 base called
destructor of 7 base called
destructor of 6 base called
destructor of 5 base called
destructor of 4 base called
destructor of 3 base called
destructor of 2 base called
destructor of 1 base called


回答5:

Yup, radioNum will be copied into m_radios. As long as you're not deallocating pointers when newradio.~Radio(); occurs (out of scope), it's OK. If m_radios, or a subclass uses pointers, you'll need to make the switch to smart pointers (shared_ptr).

When m_radios goes out of scope, the destructor is automatically called, and all the stuff std::vector holds is freed.



回答6:

In addition to the other answers that verify that the object is copied into the container when using push_back via calls to the copy constructor, you might also keep in mind the new emplace_back functionality added with c++0x.

Calling emplace_back allows you to bypass the creation of any temporary objects and have the object constructed in-place inside the container directly. emplace_back is a varardic template function that accepts the parameters you would pass to the constructor of your object, so in this case:

std::vector<Radio> m_radios;
m_radios.emplace_back(radioNum);

will not create any intermediate temporaries. This can be helpful if your objects are expensive to copy.

Hope this helps.



回答7:

By default, std::vector manages the memory itself using the copy constructor of the underlying class. So, it acts a bit as if the vector element was a local variable (when the vector goes out of scope, the element gets destructed).

When you don't want this behavior, you can use a vector of pointer or boost::ptr_vector instead.



标签: c++ vector