Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 322
Matt Munson Avatar asked Jul 16 '11 03:07

Matt Munson


4 Answers

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.

like image 137
Georg Fritzsche Avatar answered Nov 13 '22 11:11

Georg Fritzsche


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.

like image 20
Darren Engwirda Avatar answered Nov 13 '22 13:11

Darren Engwirda


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.

like image 2
Naveen Avatar answered Nov 13 '22 13:11

Naveen


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

like image 1
ribram Avatar answered Nov 13 '22 12:11

ribram