Шаг 109.
Библиотека STL.
Алгоритмы и функции классов

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

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

    Хорошим примером служит вызов алгоритма remove() для элементов списка. Алгоритм не знает, что он работает со списком, поэтому он делает то же, что в любом другом контейнере, - переупорядочивает элементы, изменяя их значения. Например, при удалении первого элемента каждому элементу присваивается значение элемента, следующего после него. Такой подход игнорирует основное достоинство списков - возможность выполнения вставки, перемещения и удаления элементов посредством модификации ссылок, а не элементов.

    Для повышения эффективности операций в списках определены специальные функции для всех модифицирующих алгоритмов. Всегда используйте их вместо алгоритмов. Более того, как показывает следующий пример, эти функции действительно исключают удаляемые элементы:

//---------------------------------------------------------------------------

#include <vcl.h>
#include <iostream>
#include <iterator>
#include <list>
#include <string>
#include <algorithm>
#include <conio.h> //необходимо для getch()
#pragma hdrstop

//---------------------------------------------------------------------------

#pragma argsused
using namespace std;
std::string ToRus(const std::string &in)
{
  char *buff = new char [in.length()+1];
  CharToOem(in.c_str(),buff);
  std::string out(buff);
  delete [] buff;
  return out;
}

int main(int argc, char* argv[])
{
  list<int> coll;
  // Вставка элементов со значениями от 6 до 1 и от 1 до 6
  for (int i=1; i<=6; ++i) {
    coll.push_front(i);
    coll.push_back(i);
  }
  // Вывод всех элементов коллекции
  cout << ToRus("Список чисел:\n");
  copy (coll.begin(), coll.end(), // Источник
    ostream_iterator<int>(cout," ")); // Приемник

  cout << endl;

  // Удаление всех элементов со значением 3
  // - неэффективно
  coll.erase (remove(coll.begin(),coll.end(),
    3), coll.end());

  // Удаление всех элементов со значением 4
  // - эффективно
  coll.remove(4);

  // Вывод всех элементов коллекции
  cout << ToRus("Измененный список чисел:\n");
  copy (coll.begin(), coll.end(), // Источник
    ostream_iterator<int>(cout," ")); // Приемник

  cout << endl;

  getch();
  return 0;
}
//---------------------------------------------------------------------------
Текст этого примера можно взять здесь.

    Результат работы программы выглядит так:


Рис.1. Результат работы приложения

    Если вы стремитесь обеспечить хорошее быстродействие, всегда используйте функции классов вместо алгоритмов. Есть только одна проблема - вы должны заранее знать, что некоторый контейнер содержит функцию, обладающую более высоким быстродействием. При вызове алгоритма remove() не последует ни предупреждепия, ни сообщения об ошибке. С другой стороны, если выбрать функцию, то при переходе на другой тип контейнера вам придется вносить изменения в программу. В справочнике алгоритмов указано, когда лучше использовать функцию классов, превосходящую алгоритм по быстродействию.

    На следующем шаге мы рассмотрим унифицированные пользовательские функции.




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