Шаг 178.
Библиотека STL. Контейнеры STL. Операции над отображениями и мультиотображениями. Вставка и удаление элементов (окончание)

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

    Чтобы удалить элемент с известным ключом, достаточно вызвать функцию erase():

  std::map<std::string,float> coll;
  .    .    .    .    .
  // Удаление всех элементов с заданным ключом 
  coll.erase(key);

    Эта версия erase() возвращает количество удаленных элементов. Для отображений возвращается либо 0, либо 1.

    Если мультиотображение содержит дубликаты, вам не удастся использовать функцию erase() для удаления только первого дубликата. Вместо этого можно воспользоваться следующим фрагментом:

  typedef std::multimap<std::string,.float> StringFloatMMap; 
  StringFloatMMap coll;
  .    .    .    .    .
  // Удаление первого элемента с переданным ключом 
  StringFloatMMap::iterator pos; 
  pos = coll.find(key); 
  if (pos != coll.end()) {
    coll.erase(pos); 
  }

    Вместо алгоритма find() используется функция класса find(), потому что она работает быстрее. Функция find() не годится для удаления элементов по известному значению (вместо ключа). Эта тема подробно обсуждается на 161 шаге.

    При удалении элементов необходима осторожность, иначе вы рискуете "отпилить ветку, на которой сидите", то есть удалить элемент, на который ссылается итератор. Пример:

  typedef std::multimap<std::string,float> StringFloatMap; 
  StringFloatMap coll; 
  .    .    .    .    .
  StringFloatMap::iterator pos;
  for (pos = coll.begin(); pos != coll.end(); ++pos) { 
    if (pos->second == value) {
      coll.erase(pos); // ОШИБКА ВРЕМЕНИ ВЫПОЛНЕНИЯ!!!
    }
  }

    После вызова erase() для элемента, на который ссылается итератор pos, итератор становится недействительным. Любые попытки использования pos после удаления элемента без повторной инициализации - даже команда ++pos - приводят к непредсказуемым последствиям.

    Если бы функция erase() всегда возвращала значение следующего элемента, проблема решалась бы просто:

  typedef std::multimap<std::string,float> StringFloatMap; 
  StringFloatMap coll; 
  StringFloatMap::iterator pos;
  .    .    .    .    .
  for (pos = coll .begin(); pos != coll.end(); ) { 
    if (pos->second == value) {
      pos = coll.erase(pos);     // Хорошо бы, но...
    }                            // происходит ОШИБКА КОМПИЛЯЦИИ!
    else {
     ++pos;
    }
  }

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

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

  typedef std::multimap<std::string,float> StringFloatMap; 
  StringFloatMap coll; 
  StringFloatMap::iterator pos;
  .    .    .    .    .
  // Удаление всех элементов с заданным значением 
  for (pos = coll.begin(); pos != coll.end(); ) { 
    if (pos->second == value) { 
      coll.erase(pos++);
    }
    else { 
      ++pos;
    }
  }

    Постфиксная команда pos++ переводит итератор pos к следующему элементу, но возвращает копию исходного значения. Следовательно, в момент вызова erase() итератор pos не ссылается на удаляемый элемент.

    На следующем шаге мы рассмотрим отображения как ассоциативные массивы.




Предыдущий шаг Содержание Следующий шаг