简体   繁体   中英

C++: vector memory corruption when modifiying an object member from outside the copy constructor but not when modifying from within

#include <iostream>
#include <vector>
#include <cassert>

class a_class
{
public:

    int num_IN;

    a_class():num_IN(0){}
    a_class(a_class const & origin){/*Initialise();*/}   //if not called here, error occurs

    void Initialise(){num_IN =5;}
};

int main () 
{
    std::vector <a_class> the_vector;

    for(int q=0; q < 30; q++)
    {
        the_vector.push_back(a_class());
        the_vector[q].Initialise();             
        assert(5 == the_vector[q].num_IN);      //no problem here
    }

    for(int q=0; q < 30; q++)
        assert(the_vector[q].num_IN == 5);      //assertion fails
}

I don't understand the difference between calling this from outside vs. inside the CC. I also don't know why it should cause a problem in any case.

std::vector may reallocate the buffer it uses if it's size outgrows it, in which case it has to copy the old elements over to the new buffer. If you don't have a proper copy constructor that copies num_IN over, the old value is lost.

Fix this by providing a proper copy constructor:

a_class(a_class const & origin) : num_IN(origin.num_IN) {}

In the code posted the copy constructor isn't even needed - if you don't provide one the compiler will generate a suitable one here.

The std::vector class may have to reallocate the underlying dynamic array when you are repeatedly calling push_back() to append new elements. The usual strategy is for std::vector to increase the size of the underlying buffer by a factor, possibly a factor of 2 .

When this reallocation does occur, the copy constructor (or the move constructor if you've defined one and are using c++0x ) is called to copy the vector's elements from the old buffer to the new one.

Your copy constructor is not actually copying properly, you should be assigning the num_IN parameter:

a_class(a_class const& src): num_IN(src.num_IN) {}

Typically, with STL containers, the data types stored should obey the " rule of three " in that the constructor, copy constructor, assignment operator and destructor all work together robustly.

With move semantics in c++0x I guess this should be extended to the "rule of five" in that you should also consider properly defined move constructors and move assignment operators.

The problem is because of the push_back , your vector reallocates the memory in the first for loop whenever vector's size increases beyond its capacity . During this reallocation vector copies the objects already present in it by invoking the copy constructor. Since your copy constructor is not correct (You are not doing anything there), the initialized value is not carried over during reallocation.

Your copy constructor isn't doing anything. It should be saying num_IN = origin.num_IN;

AHhh,

I can see what is happening.

In the first loop you are pushing back object to the vector, each time you push back the vector is resized and copy constructor is called on all the elements in the vector already. Since you have overwritten the copy constructor not to do anything, num_IN is left uninitialized.

to make it work. i would reenable to copy constructor to do member wise copy.

Alternatively.

i would resize the vector to the required size. This way, vector is not resized each time you add an extra element

std::vector <a_class> the_vector;
the_vector.resize(30);

for(int q=0; q < 30; q++)
{
    the_vector[q].Initialise();             
    assert(5 == the_vector[q].num_IN);      //no problem here
}

for(int q=0; q < 30; q++)
    assert(the_vector[q].num_IN == 5);      //assertion fails

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM