Бьерн Страуструп - Абстракция данных в языке С++

ОГЛАВЛЕНИЕ

 

Разграничение доступа к данным

      Рассмотрим фрагмент старого С (*3), представляющий реализацию
    концепции даты:

        struct date { int  day, month, year;}; 
        struct date today;
        extern void set_date ();
        extern void next_date ();
        extern void next_today ();
        extern void print_date ();

      В приведенном примере нет явной связи между функциями и типом данных, нет
    и указаний на то, что данные функции должны быть единственными, которые
    имеют доступ к членам структуры d a t e . Необходимо же иметь
    возможность это указать.
____________
    (*1) - В оригинале - "friends"ма frienфа functions - прим.переводчика.

    (*2) - В оригинале - deriveфа class, что можно перевести как
          "порождаемый". - прим.переводчика.
    (*3) - Ключевое слово void определяет функцию, не возвращающую
            значения. Оно было введено в С примерно в 1980 г.

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

        date my_birthday,  today;
        set_date (&my_birthday, 30, 12, 1950);
        set_date (&today, 23, 6, 1983);
        print_date (&today);
        next_date (&today);

      Дружественные функции определяются обычным образом. Например :

        void next_date  (date* d) 
        {
              if <++d->
                    /* особый случай */
              }
        }

      Такое решение проблемы упрятывания информации просто и часто
    достаточно эффективно. Оно не вполне гибко поскольку допускает
    доступ всем дружественным функциям к всем переменным типа
    Например, невозможно иметь различный набор дружественных функций
    для данных m y _ b i r t h d a y . Функция однако может быть
    дружественной более чем одному классу. Важность этого будет
    продемонстрирована в секции 19. Нет требования, чтобы
    дружественная функция могла манипулировать только переменнымим,
    передаваемыми как аргументы. Напримерм, в функцию может быть
    встроено имя глобальной переменной:

        void next_today ()  
        {
              if <++today.day>
                    /* особый случай */
              }
        }

      Защита данных от функций, не являющихся дружественными, основана на
    ограничении использования имен членов класса. Поэтому она может быть
    обойдена путем адресной манипуляции и явного преобразования типов.
      Можно получить несколько преимуществ от предоставления доступа к
    данным структуры только явно указанному списку функций.
      Любая ошибка, вызывающая недопустимое состояние переменной типа
    d a t e , должна быть вызвана кодом дружественной функции поэтому
    первый шаг отладки - локализация - будет завершен еще до того
    как программа будет исполнена. Это особый случай общей идеи, что
    любое изменение поведения типа d a t e может и должно быть вызвано
    изменениями в его дружественных функциях. Другое преимущество
    состоит в том, что потенциальному пользователю для того, чтобы
    научиться пользоваться таким типом нужно изучить только описания
    дружественных функций. Опыт С++ вполне это подтверждает.