C++ Vector удаляет определенные элементы, но segfaults

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

У меня есть собственный класс HostEntry, который содержит необходимую информацию, такую ​​как хост назначения, хост для блокировки, IP-адрес хоста назначения... и т.д.

В классе HostsManager он хранит вектор для отслеживания всех добавленных хостов. Чтобы полностью заблокировать хост, я должен добавить example.com И www.example.com, но когда я перебираю вектор, он удаляет только запись, начинающуюся с «www». и оставляет тот без. Если вы попытаетесь удалить его во второй раз (только в записи отсутствует «www.»), произойдет ошибка сегментации, и я не знаю, почему.

void HostsManager::delHost(std::string blockedhost) {
    strip(blockedhost);
    string tmp; // yes I know it's not great practice to do it like this, but it was for debug reasons
    for (vector<HostEntry>::iterator viter = hosts.begin(); viter != hosts.end(); ++viter) {
        tmp = viter->getHost();
        if (tmp == blockedhost || tmp == ("www." + blockedhost)) {
            viter = hosts.erase(viter);
        }
    }
}

Пример вызова этой конкретной функции:

HostsManager mgr;
mgr.delHost("mysite.com"); // this deletes "www.mysite.com" but not "mysite.com" - whether or not you call delHost() with the "www." prefix
mgr.delHost("mysite.com"); // if you call it a second time, it segfaults O.o

Помощь в этом была бы БОЛЬШОЙ признательна.

РЕДАКТИРОВАТЬ: я присвоил значение, возвращаемое вызовом erase(), для viter, тот же результат. Я до сих пор понятия не имею, почему это происходит.

Если вам нужен весь код, он находится по адресу http://paste.pocoo.org/show/363051/< /а>


person FurryHead    schedule 31.03.2011    source источник


Ответы (5)


Обычно лучше использовать std::remove_if для удаления нескольких элементов из вектора; он выполняется за линейное время, а не за квадратичное, и позволяет не беспокоиться об аннулировании итератора.

Может выглядеть примерно так:

hosts.erase(std::remove_if(hosts.begin(), 
                           hosts.end(), 
                           [&blockedhost](const HostEntry& entry) {
                               return entry.getHost() == blockedhost || 
                                      entry.getHost() == "www." + blockedhost;
                           }),
            hosts.end());

Вы можете сделать то же самое без лямбда-выражений C++0x через структуру для сравнения:

struct RemoveBlockedHost {
    RemoveBlockedHost(const std::string& s): blockedHost(s) {}
    bool operator () (const HostEntry& entry) {
        return entry.getHost() == blockedHost || entry.getHost() == "www." + blockedHost;
    }
    const std::string& blockedHost;
};

hosts.erase(std::remove_if(hosts.begin(), hosts.end(), RemoveBlockedHost(blockedhost)), hosts.end());
person Peter    schedule 31.03.2011
comment
Я хочу удалить только два элемента - оба содержат определенный хост, один с www. префикс, один без. Будет ли это удалять только записи хоста, содержащие blockedhost ? похоже, что он удалит с первого индекса заблокированного хоста до конца. - person FurryHead; 31.03.2011
comment
предполагая, что я не испортил свой синтаксис, он должен стереть только те элементы, которые вы хотите. remove_if перемещает в конец все элементы, соответствующие предикату; затем он стирает из возвращаемого итератора до конца вектора. - person Peter; 31.03.2011
comment
Аааа, ок. Кроме того, допустим ли синтаксис paste.pocoo.org/show/363058? Я, вероятно, воспользуюсь вашим примером с лямбадой. - person FurryHead; 31.03.2011
comment
Я предполагаю, что это сработает, хотя на самом деле это не самое элегантное решение — делать копию всего контейнера без записей, которые вы хотите стереть. - person Peter; 31.03.2011

Вы удаляете элементы из вектора, который вы повторяете! Вы хотите установить viter в значение, возвращенное из erase. См. документацию C++ для vector::erase.

person Gian Paolo    schedule 31.03.2011
comment
Ааа, спасибо. Я совершенно забыл, что плохо изменять список/вектор, который вы повторяете. Я попробую это и посмотрю, исправит ли это это. - person FurryHead; 31.03.2011
comment
Увы, не получилось. У меня точно такая же проблема. Код в сообщении теперь обновлен. - person FurryHead; 31.03.2011

Когда вы удаляете элемент вектора, он делает недействительным итератор.

Std::lists разработан, чтобы позволить вам удалять в середине, как это, не делая недействительным ваш итератор. Таким образом, для этой конкретной операции можно было бы использовать простейший код.

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

person rotanimod    schedule 31.03.2011
comment
Действительно, я планирую иметь 200+ предметов. Я думаю, что вектор будет работать, но использование viter = hosts.erase(viter) все еще не работает. - person FurryHead; 31.03.2011

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

person Oswald    schedule 31.03.2011

Я не вижу никакой ошибки в коде. Я думаю, вам следует запустить отладочную версию и поставить точки останова в delHost(). Пройдитесь по нему, чтобы выяснить, в чем причина проблемы с ошибкой seg.

person MorrisLiang    schedule 31.03.2011