Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

what happens to old values of std::vector, when I do std::vector = std::vector

Tags:

c++

c++11

vector

I have following code snippet:

std::vector<std::string> v1;
v1.push_back("Hello");
v1.push_back("World");

std::vector<std::string> v2;
v2.push_back("Good");
v2.push_back("Bye");

v1 = v2;

What will happen to the old values of vector v1.

  • Old values will be destructed immediately?

or

  • They will destroy when vector v1 destroys. Then How can I free them immediately?
like image 317
Anil8753 Avatar asked Jan 15 '16 05:01

Anil8753


2 Answers

According to the references

Any elements held in the container before the call are either assigned to or destroyed.

So, in your example, the original underlying std::string values will have the std::string::operator= called on them and they (the underlying std::string itself) will not be destroyed.

If the vector being assigned to is larger than the vector being assigned from, then you would have the destructor called for the elements in the vector, example:

std::vector<std::string> v1;
v1.push_back("Hello");
v1.push_back("There");
v1.push_back("World");

std::vector<std::string> v2;
v2.push_back("Good");
v2.push_back("Bye");

v1 = v2;

In this example, the std::string values for "Hello" and "There" will be assigned "Good" and "Bye" respectively while the 3rd string of "World" will be destroyed.

Remember though this applies only to the element contained in the vector, take the following code for example:

std::vector<int*> v1;
v1.push_back(new int(42));
v1.push_back(new int(24));
v1.push_back(new int(38));

std::vector<int*> v2;
v2.push_back(new int(32));
v2.push_back(new int(23));

v1 = v2;

In this code, a memory leak occurs since v1 is a vector containing pointers, the pointers themselves are assigned/destroyed, not the value it points to. This example is equivalent to the following:

int* x = new int(42);
int* y = new int(24);
x = y; // memory leak here

To this, if you wish to remove the elements from the vector (thus calling the destructor of each element in the container), you can use the clear method, which clears all elements from the vector, or you can also use the erase method, which can erase a single element or multiple.

The same rules apply with clear and erase; if the underlying type is a pointer type that has claimed memory, you need to be sure you delete the elements in the vector; example:

template < typename IteratorType >
void delete_elements(IteratorType start, IteratorType end)
{
    while (start != end) {
        delete (*start); // or delete[]
        ++start;
    }
}

std::vector<int*> v1;
v1.push_back(new int(42));
v1.push_back(new int(24));
v1.push_back(new int(38));

delete_elements(v1.begin(), v1.end());

And if you want to see this assignment in action, you can use a little test class like the following code:

#include <iostream>
#include <string>
#include <vector>

#define outi(v) std::cout << v << ", i = " << i << std::endl

class tester {
    public:
        tester() : i(42) { outi("default ctor"); }
        tester(int x) : i(x) { outi("implicit ctor"); }
        ~tester() { outi("dtor"); }
        tester(const tester& cp) : i(cp.i) { outi("copy ctor"); }
        tester& operator=(const tester& cp) {
            this->i = cp.i;
            outi("operator=");
            return *this;
        }
        // for std::cout << tester;
        friend std::ostream& operator<<(std::ostream& o, const tester& t) {
            o << t.i; return o;
        }
    private:
        int i;
};

template < typename itr >
static void printall(itr start, itr end)
{
    while (start != end) {
        std::cout << (*start) << std::endl;
        ++start;
    }
}

int main(int argc, char* argv[])
{
    std::vector<tester> v1;
    std::cout << "create v1 elements" << std::endl;
    v1.push_back(10); // implicit ctor calls
    v1.push_back(24);
    v1.push_back(33);
    printall(v1.begin(), v1.end());
    std::vector<tester> v2;
    std::cout << "create v2 elements" << std::endl;
    v2.push_back(tester(100));
    v2.push_back(tester(99));
    printall(v2.begin(), v2.end());
    std::cout << "v1 = v2" << std::endl;
    //v1.clear(); // uncomment to call dtor's
    v1 = v2;
    printall(v1.begin(), v1.end());
    return 0;
}

Where the output of this program might be something like the following:

create v1 elements
implicit ctor, i = 10
copy ctor, i = 10
dtor, i = 10
implicit ctor, i = 24
copy ctor, i = 10
dtor, i = 10
copy ctor, i = 24
dtor, i = 24
implicit ctor, i = 33
copy ctor, i = 10
copy ctor, i = 24
dtor, i = 10
dtor, i = 24
copy ctor, i = 33
dtor, i = 33
10
24
33
create v2 elements
implicit ctor, i = 100
copy ctor, i = 100
dtor, i = 100
implicit ctor, i = 99
copy ctor, i = 100
dtor, i = 100
copy ctor, i = 99
dtor, i = 99
100
99
v1 = v2
operator=, i = 100
operator=, i = 99
dtor, i = 33
100
99
dtor, i = 100
dtor, i = 99
dtor, i = 100
dtor, i = 99

I say might because your compiler could choose to order the constructors/assignment slightly different, but the results will be the same.

I hope that can help.

like image 130
txtechhelp Avatar answered Sep 28 '22 09:09

txtechhelp


The old values of v1 are destroyed when v1 is assigned to, because the assignment removes the earlier content.

To clear a vector immediately without assigning a new value, you can use the clear method or the resize method.

like image 22
Cheers and hth. - Alf Avatar answered Sep 28 '22 08:09

Cheers and hth. - Alf