Бьерн Страуструп - Абстракция данных в языке С++ - Разграничение доступа к данным
ОГЛАВЛЕНИЕ
Разграничение доступа к данным
Рассмотрим фрагмент старого С (*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 может и должно быть вызвано
изменениями в его дружественных функциях. Другое преимущество
состоит в том, что потенциальному пользователю для того, чтобы
научиться пользоваться таким типом нужно изучить только описания
дружественных функций. Опыт С++ вполне это подтверждает.