У меня есть странная проблема, которая редко случается с недействительными итераторами STL, которые я упростил в приведенном ниже примере кода.
#include "Bar.h"#include <map>
class Foo
{
std::map<int, Bar*> test;
public:
Foo() {}
void Update();
void AddEntry(int i, Bar* bar);
void DeleteEntry(int i);
};
#include "Foo.h"
void Foo::Update() {
for(auto iter = test.rbegin(); iter != test.rend(); iter++) {
iter->second->DoThingOne();
iter->second->DoThingTwo(); // BREAKS ON 2nd ITERATION
}
}
void Foo::AddEntry(int i, Bar* b) {
test[i] = b;
}
void Foo::DeleteEntry(int i) {
delete test[i];
test.erase(i);
}
class Foo;
class Bar
{
Foo* f;
static int count;
public:
friend class Foo;
Bar(Foo* f_);
void DoThingOne();
void DoThingTwo();
};
#include "Bar.h"#include "Foo.h"
int Bar::count = 0;
Bar::Bar(Foo* f_) : f(f_) {}
void Bar::DoThingOne() {
if(count++ == 1) {
f->DeleteEntry(3);
}
}
void Bar::DoThingTwo() {
// Does things
}
#include "Foo.h"
int main() {
Foo* foo = new Foo();
Bar* one = new Bar(foo);
Bar* two = new Bar(foo);
Bar* three = new Bar(foo);
foo->AddEntry(1, one);
foo->AddEntry(2, two);
foo->AddEntry(3, three);
foo->Update();
return 0;
}
Так что в основном, когда Foo::Update
1-я итерация цикла выполняется нормально, затем 2-я итерация вызывает DoThingOne, который удаляет запись в карте, которую только что использовала предыдущая итерация цикла. когда DoThingTwo
вызывается сразу после того, как я получаю сообщение об ошибке «Отладочное утверждение не удалось!
Из того, что я понимаю, итераторы над картами и наборами всегда действительны, за исключением итераторов, ссылающихся на удаленные элементы, но здесь итератор ссылается на элемент сразу после удаляемого элемента. Я могу только предположить, что это связано с тем, что удаляемый элемент является первым / последним элементом, а используемый итератор ссылается на новый первый / последний элемент, но я до сих пор не могу точно выяснить, почему это происходит. или как обойти это. У меня действительно есть возможность обнаружить, когда это происходит в цикле for перед вызовом DoThingTwo, и попытаться исправить это там.
Редактировать: просмотрев ссылку, предоставленную Nemo, я изменил цикл следующим образом, и он, кажется, работает:
void Foo::Update {
auto iter = test.end();
for(iter--; iter != test.begin(); iter--) {
iter->second->DoThingOne();
iter->second->DoThingTwo();
}
iter->second->DoThingOne();
iter->second->DoThingTwo();
}
Это выглядит очень неряшливо, но это делает работу. Поскольку использование итератора вместо reverse_iterator работает, я предполагаю, что оно как-то связано с тем, как работает reverse_iterator по сравнению с итератором.
Задача ещё не решена.