С++ удаляет элемент списка во время итерации: стандартное решение не работает?

Вот моя проблема. Я прочитал много предыдущих вопросов о том, как удалить член списка во время итерации по нему, и я попробовал различные решения, которые предлагали ответы. Бывает, что они, похоже, не работают. У меня есть список классов такого рода:

class Walker {
 public:
 Walker(int); 
 ~Walker(); 
 ****** *x; 
 ****** *y; 
 ****** *z; 
 ****** weight; 
 int molteplicity; 
};

Конструктор и деструктор следующие

Walker::Walker(int particle_num) {
 x = new ******[particle_num];
 y = new ******[particle_num];
 z = new ******[particle_num];
}
Walker::~Walker() {
 delete x;
 delete y;
 delete z;
}

Теперь список

list<walker> population;
</walker>

определяется как член другого класса. Теперь, если элемент molteplicity равен null (вычисляется с помощью другой функции), я должен динамически удалить член из класса, и так я это делаю:

for( it = population.begin(); it != population.end(); ) {
 if( it->molteplicity == 0 ) {
 it = population.erase(it);
 } else {
 ++it;
 }

получить следующую ошибку во время выполнения:

prog (22332) malloc: * ошибка для объекта 0x7f838ac03a60: указатель, являющийся освобождение не было выделено * установить точку останова в malloc_error_break для отладки ловушки прерывания: 6

Вы видите ошибку? Большое спасибо за Вашу помощь!! Если вам нужен еще один код, просто дайте мне знать.

2 ответа

Вам следует реализовать конструктор ,, потому что список использует его внутри. Копирование должно выполняться при выполнении кода типа: list.push_back(Walker(5));. Временный объект должен быть перемещен или скопирован в список. Конструктор копии по умолчанию копирует только указатели, поэтому деструктор освобождает одну и ту же память дважды.

Также перемещать семантику в этом случае достаточно: Добавьте этот конструктор в свой код:

Walker(Walker&& other)
{
 x = other.x;
 y = other.y;
 z = other.z;
 weight = other.weight;
 molteplicity = other.molteplicity;
 //remove data from the original object to avoid freeing memory twice
 other.x = nullptr;
 other.y = nullptr;
 other.z = nullptr;
}

и удалите конструктор копирования (или выполните его правильно):

Walker(const Walker& other) = delete;

Если вы используете указатели и выделяете память, вы должны знать правило три:

Правило трех (также известное как Закон Большой тройки или Большой Три) - это эмпирическое правило на С++ (до С++ 11), утверждающее, что если класс определяет одно из следующего, это должно явно явно определить все три:

деструктор

конструктор копирования

оператор присваивания копий


Проблема не имеет ничего общего с использованием std::list, но в деструкторе:

Walker::~Walker() {
 delete x;
 delete y;
 delete z;
}

Вы выделили с помощью new[], а не new, поэтому вы должны использовать delete[] not delete:

Walker::~Walker() {
 delete[] x;
 delete[] y;
 delete[] z;
}

Живая демонстрация

Обратите внимание, что molteplicity и weight никогда не инициализируются и поэтому могут содержать любое число (вероятно, отличное от 0).

После этих изменений программа отлично компилируется и запускается.

Также обратите внимание, что new и delete обычно недовольны в сообществе С++ по очень веским причинам. Используйте интеллектуальные указатели или контейнеры и, пожалуйста, обычно следуйте правилу 0.

И, наконец, вы можете достичь более чистого решения, используя std::list::remove_if. Если вы последуете этим советам, вы получите что-то вроде:

struct Walker {
 Walker(int num) 
 : x(num), y(num), z(num)
 , weight(0)
 , molteplicity(0)
 {}
 std::vector<******> x, y, z; 
 ****** weight; 
 int molteplicity; 
};
</******>

и используется как:

std::list<walker> population {...};
population.remove_if([](Walker const& w) { return w.molteplicity == 0; });
</walker>

Живая демонстрация

Это более читаемо и правильнее.

licensed under cc by-sa 3.0 with attribution.