Бьерн Страуструп - Язык программирования С++. Главы 5-8
Страница 62. Индексация



7.7 Индексация

 Операторная функция operator[] задает для объектов классов
 интерпретацию индексации. Второй параметр этой функций (индекс) может
 иметь произвольный тип. Это позволяет, например, определять
 ассоциативные массивы. В качестве примера можно переписать
 определение из $$2.3.10, где ассоциативный массив использовался
 в небольшой программе, подсчитывающей число вхождений слов в файле.
 Там для этого использовалась функция. Мы определим настоящий тип
 ассоциативного массива:

           class assoc {
              struct pair {
                 char* name;
                 int val;
              };

              pair* vec;
              int max;
              int free;

              assoc(const assoc&);            // предотвращает копирование
              assoc& operator=(const assoc&); // предотвращает копирование
           public:
              assoc(int);
              int& operator[](const char*);
              void print_all();
            };

 В объекте assoc хранится вектор из структур pair размером max.
 В переменной free хранится индекс первого свободного элемента
 вектора.
     Чтобы предотвратить копирование объектов assoc, конструктор
 копирования и операция присваивания описаны как частные. Конструктор
 выглядит так:

            assoc::assoc(int s)
            {
              max = (s<16) ? 16 : s;
              free = 0;
              vec = new pair[max];
            }

 В реализации используется все тот же неэффективный алгоритм поиска,
 что и в $$2.3.10. Но теперь, если вектор переполняется, объект
 assoc увеличивается:

            #include <string.h>

            int& assoc::operator[](const char* p)
            /*
              работает с множеством пар (структур pair):
              проводит поиск p, возвращает ссылку на
              целое значение из найденной пары,
              создает новую пару, если p не найдено
            */
            {
              register pair* pp;

              for (pp=&vec[free-1]; vec<=pp; pp-- )
                  if (strcmp(p,pp->name) == 0) return pp->val;
              if (free == max) { //переполнение: вектор увеличивается
                 pair* nvec = new pair[max*2];
                 for (int i=0; i<max; i++) nvec[i] = vec[i];
                 delete vec;
                 vec = nvec;
                 max = 2*max;
              }

              pp = &vec[free++];
              pp->name = new char[strlen(p)+1];
              strcpy(pp->name,p);
              pp->val = 0;    // начальное значение = 0
              return pp->val;
            }

 Поскольку представление объекта assoc скрыто от пользователя, нужно
 иметь возможность напечатать его каким-то образом. В следующем разделе
 будет показано как определить настоящий итератор для такого объекта.
 Здесь же мы ограничимся простой функцией печати:

            void assoc::print_all()
            {
              for (int i = 0; i<free; i++)
                  cout << vec[i].name << ": " << vec[i].val << '\n';
            }

 Наконец, можно написать тривиальную программу:

            main() // подсчет числа вхождений во входной
                   // поток каждого слова
            {
              const MAX = 256;  // больше длины самого длинного слова
              char buf[MAX];
              assoc vec(512);
              while (cin>>buf) vec[buf]++;
              vec.print_all();
            }

 Опытные программисты могут заметить, что второй комментарий можно
 легко опровергнуть. Решить возникающую здесь проблему предлагается
 в упражнении $$7.14 [20]. Дальнейшее развитие понятие ассоциативного
 массива получит в $$8.8.
     Функция operator[]() должна быть членом класса. Отсюда следует,
 что эквивалентность x[y] == y[x] может не выполняться, если
 x объект класса. Обычные отношения эквивалентности, справедливые
 для операций со встроенными типами, могут не выполняться для
 пользовательских типов ($$7.2.2, см. также $$7.9).

 
« Предыдущая статья   Следующая статья »