Вектор C ++, удаляющий определенные элементы, но с ошибками - PullRequest
1 голос
/ 31 марта 2011

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

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

В классе HostsManager он хранит вектор для отслеживания всех добавленных хостов.Чтобы полностью заблокировать хост, я должен добавить example.com AND 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/

Ответы [ 5 ]

2 голосов
/ 31 марта 2011

Обычно лучше использовать 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 lambdasчерез структуру для сравнения:

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());
1 голос
/ 31 марта 2011

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

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

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

1 голос
/ 31 марта 2011

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

0 голосов
/ 31 марта 2011

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

0 голосов
/ 31 марта 2011

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

...