C ++: векторное повреждение памяти при модификации элемента объекта извне конструктора копирования, но не при изменении изнутри - PullRequest
4 голосов
/ 16 июля 2011
#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
}

Я не понимаю разницы между вызовом этого извне и внутри ЦК. Я также не знаю, почему это должно вызвать проблемы в любом случае.

Ответы [ 5 ]

4 голосов
/ 16 июля 2011

std::vector может перераспределить буфер, который он использует, если его размер превышает его, и в этом случае он должен скопировать старые элементы в новый буфер.Если у вас нет подходящего конструктора копирования, который копирует num_IN, старое значение теряется.

Исправьте это, предоставив правильный конструктор копирования:

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

В кодеопубликованный конструктор копирования даже не нужен - если вы его не предоставите, компилятор сгенерирует подходящий здесь.

3 голосов
/ 16 июля 2011

Классу std::vector может потребоваться перераспределить базовый динамический массив, когда вы повторно вызываете push_back() для добавления новых элементов. Обычная стратегия заключается в том, чтобы std::vector увеличивал размер базового буфера с коэффициентом, возможно, с коэффициентом 2.

Когда происходит такое перераспределение, вызывается конструктор копирования (или конструктор перемещения, если вы его определили и используете c++0x) для копирования элементов вектора из старого буфера в новый.

Ваш конструктор копирования на самом деле не копирует должным образом, вы должны назначить параметр num_IN:

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

Как правило, в контейнерах STL хранимые типы данных должны подчиняться «правилу из трех », так как конструктор, конструктор копирования, оператор присваивания и деструктор работают надежно.

С семантикой перемещения в c++0x Полагаю, это должно быть расширено до "правила пяти" в том смысле, что вы также должны учитывать правильно определенные конструкторы перемещения и операторы присваивания перемещения.

2 голосов
/ 16 июля 2011

Проблема заключается в том, что push_back, ваш вектор перераспределяет память в первом цикле for всякий раз, когда size вектора выходит за пределы его capacity.Во время этого перераспределения vector копирует объекты, уже присутствующие в нем, вызывая конструктор копирования.Поскольку ваш конструктор копирования неверен (вы ничего не делаете там), инициализированное значение не переносится во время перераспределения.

1 голос
/ 16 июля 2011

аааа,

Я вижу, что происходит.

В первом цикле вы перемещаете объект назад к вектору, каждый раз, когда вы перемещаете его назад, вектор изменяется, и конструктор копирования вызывается для всех элементов вектора. Поскольку вы переписали конструктор копирования, чтобы ничего не делать, num_IN остается неинициализированным.

чтобы все заработало. я бы смог скопировать конструктор, чтобы сделать копию для членов.

В качестве альтернативы.

Я бы изменил размер вектора до необходимого размера. Таким образом, вектор не изменяется каждый раз, когда вы добавляете дополнительный элемент

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
1 голос
/ 16 июля 2011

Ваш конструктор копирования ничего не делает.Стоит сказать, что num_IN = origin.num_IN;

...