суббота, 5 декабря 2009 г.

Классы и абстрагирование данных

Цели
        Понять принципы инкапсуляции и скрытия данных при разрабоке программного обеспечения.
        Понять идеи абстракции данных и абстрактных типов данных (АТД).
        Научиться создавать АТД С++, а именно, классы.
        Понять, как создаются, используются и разрушаются объекты классов.
        Научиться управлять доступом к данным-элементам и функциям-элементам.
        Начать ценить значение объектной ориентации.

                Введение
                Определения структур
               Доступ к элементам структуры
                Использование определенного пользователем типа Time с помощью Struct
                Использование абстрактного типа данных Time с помощью класса
                Область действия класс и доступ к элементам класса
                Отделение интерфейса от реализации
                Управление доступом к элементам
                Функции доступа и обслуживающие функции-утилиты
                Инициализация объектов класса: конструкторы
                Использование конструкторов с аргументами по умолчанию
                Использование деструкторов
                Когда вызываются конструкторы и деструкторы
                Использование данных-элементов и функций-элементов
               Тонкий момент: возвращение ссылки на закрытые данные-элементы
                Присваивание побитовым копированием по умолчанию
                Повторное использование программного обеспечения
                Размышления об объектах: программирование классов для модели­рования лифта.
Резюме Терминология Типичные ошибки программировани Хороший стиль программирования Советы по повышению эффективности Заме­чания по технике программирования Упражнения для самопроверки Ответы на упражнения для самопроверки Упражнения

6.1. Введение

Теперь мы начнем знакомиться с объектной ориентацией в С++. Почему мы отложили объектно-ориентированное программирование (ООП) на С++ до главы 6? Дело в том, что объекты, которые мы будем строить, будут составлены частично из структурированных фрагментов программ, так что сначала нам нужно было определить основы структурного программирования.
В разделах «Размышления об объектах» в конце глав с 1 по 5 мы по­знакомились с основными принципами (т.е. с «объектным мышлением») и с терминологией (т.е. с «объектным языком») объектно-ориентированного программирования на С++. В этих специальных разделах мы также обсудили технику объектно-ориентированного проектирования: мы проанализировали типичную постановку задачи, которая требовала построения системы (напри­мер, модели лифта), определили, какие объекты необходимы для реализации системы, определили, какие атрибуты должны иметь объекты, определили, какими вариантами поведения должны обладать эти объекты, и указали, как объекты должны взаимодействовать друг с другом для достижения гло­бальной цели системы.
Давайте проведем краткий обзор некоторых ключевых принципов и тер­минологии объектной ориентации. ООП инкапсулирует данные (атрибуты) и функции (варианты поведения) в совокупности, называемые объектами; данные и функции объекта тесно связаны друг с другом. Объекты обладают свойством скрытия информации. Это значит, что хотя объекты могут знать, как связываться друг с другом посредством хорошо определенного интер­фейса, им обычно не позволено знать, как реализуются другие объекты — детали реализации спрятаны внутри самих объектов. Несомненно, можно ездить на автомобиле, не зная технических деталей его внутреннего функ­ционирования — трансмиссии, выхлопной трубы и др. Мы увидим, почему скрытие информации так важно для разработки хорошего программного обес­печения.
В С и других процедурно-ориентированных языках программирование стремится быть ориентированным на действия, тогда как в идеале програм­мирование на С++ объектно-ориентированное. В С единицей программиро­вания является функция. В С++ единицей программирования является класс, на основе которого в конечном счете создаются объекты.
Программисты на С основное внимание уделяют написанию функций. Группы действий, выполняющие некоторую задачу, объединяются в функ­ции, а функции группируются, чтобы образовать программу. Данные несо­мненно важны в С, но современная точка зрения состоит в том, что данные существуют в первую очередь для поддержки действий, выполняемых функ­циями. Глаголы в описании проектируемой системы помогают программисту на С определить множество функций, которые будут совместно работать для реализации системы.
Программисты на С++ основное внимание уделяют созданию своих соб­ственных определяемых пользователем типов, называемых классами. Клас­сы — это типы, определяемые программистом. Каждый класс содержит дан­ные и набор функций, манипулирующих с этими данными. Компоненты-данные класса называются данными-элементами. Компоненты- функции класса называются функциями-элементами. Подобно тому как сущ­ность встроенного типа, такого, как int, называется переменной, сущность определяемого пользователем типа (т.е. класса) называется объектом. Цент­ром внимания в С++ являются не функции, а объекты. Имена существи­тельные в описании проектируемой системы помогают программисту на С++ определить множество классов. Эти классы используются для создания объ­ектов, которые будут совместно работать для реализации системы.
Классы в С++ являются естественным продолжением структуры struct в С. Прежде чем рассматривать специфику разработки классов на С++, мы обсудим структуры и построим определенный пользователем тип, основанный на структуре. Слабости, которые мы выявим в этом подходе, помогут объ­яснить запись класса.

6.2. Определения структур

Структуры это составные типы данных, построенные с использованием других типов. Рассмотрим следующее определение структуры:
struct Time {
int hour; // 0-23 int minute; // 0-59 int second; // 0-59
} ;
Ключевое слово struct начинает определение структуры. Идентификатор Timeтег (обозначение, имя-этикетка) структуры. Тэг структуры исполь­зуется при объявлении переменных структур данного типа. В этом примере имя нового типа Time. Имена, объявленные в фигурных скобках описания структуры это элементы структуры. Элементы одной и той же структуры должны иметь уникальные имена, но две разные структуры могут содержать не конфликтующие элементы с одинаковыми именами. Каждое определение структуры должно заканчиваться точкой с запятой. Приведенное объяснение, как мы вскоре увидим, верно и для классов.
Определение Time содержит три элемента типа inthour, minute и second (часы, минуты и секунды). Элементы структуры могут быть любого типа и одна структура может содержать элементы многих разных типов. Структура не может, однако, содержать экземпляры самой себя. Например, элемент типа Time не может быть объявлен в определении структуры Time. Однако, может быть включен указатель на другую структуру Time. Структура, содержащая элемент, который является указателем на такой же структурный тип, называ­ется структурой с самоадресацией. Структуры с самоадресацией полезны для формирования связных структур данных (см. главу 15).
Предыдущее определение структуры данных не резервирует никакого пространства в памяти; определение только создает новый тип данных, ко­торый используется для объявления переменных. Переменные структуры объ­являются так же, как переменные других типов. Объявление
Time timeObject, timeArray[10], *timePtr;
объявляет timeObject переменной типа Time, timeArrayмассивом с 10 элементами типа Time, a timePtrуказателем на объект типа Time.

6.3. Доступ к элементам структуры

Для доступа к элементам структуры (или класса) используются операции доступа к элементам операция точка (.) и операция стрелка (->). Операция точка обращается к элементу структуры (или класса) по имени переменной объекта или по ссылке на объект. Например, чтобы напечатать элемент hour структуры timeObject используется оператор
cout << timeObject.hour;
Операция стрелка, состоящая из знака минус (-) и знака больше (>), записанных без пробела, обеспечивает доступ к элементу структуры (или класса) через указатель на объект. Допустим, что указатель timePtr был уже объявлен как указывающий на объект типа Time и что адрес структуры timeObject был уже присвоен timePtr. Тогда, чтобы напечатать элемент hour структуры timeObject с указателем timePtr, можно использовать оператор
cout << timePtr->hour;
Выражение timePtr->hour; эквивалентно (*timePtr).hour, которое разы­меновывает указатель и делает доступным элемент hour через операцию точка. Скобки нужны здесь потому, что операция точка имеет более высокий приоритет, чем операция разыменования указателя (*). Операции стрелка и точка наряду с круглыми и квадратными скобками имеют второй наивысший приоритет (после операции разрешения области действия, введенной в главе 3) и ассоциативность слева направо

6.4. Использование определенного пользователем типа Time с помощью Struct

Программа на рис. 6.1 создает определенный пользователем тип структуры Time с тремя целыми элементами: hour, minute и second. Программа опреде­ляет единственную структуру типа Time, названную dinnerTime, и использует операцию точка для присвоения элементам структуры начальных значений 18 для hour, 30 для minute и 0 для second. Затем программа печатает время в военном (24-часовом) и стандартном (12-часовом) форматах. Заметим, что функции печати принимают ссылки на постоянные структуры типа Time. Это является причиной того, что структуры Time передаются печатающим функ­циям по ссылке — этим исключаются накладные расходы на копирование, связанные с передачей структур функциям по значению, а использование const предотвращает изменение структуры типа Time функциями печати. В главе 7 мы обсудим объекты const и функции-элементы const.
Обычно структуры передаются вызовом по значению. Чтобы избежать накладных
расходов на копирование структуры, передавайте структуры вызовом по ссылке.
Чтобы избежать накладных расходов вызова по значению и вдобавок получить
выгоды защиты исходных данных от изменения, передавайте аргументы большого
размера как ссылки const.
Существуют препятствия созданию новых типов данных указанным спо­собом с помощью структур. Поскольку инициализация структур специально не требуется, можно иметь данные без начальных значений и вытекающие отсюда проблемы. Даже если данные получили начальные значения, воз­можно, это было сделано неверно. Неправильные значения могут быть при­своены элементам структуры (как мы сделали на рис. 6.1), потому что про­грамма имеет прямой доступ к данным. Программа присвоила плохие значения всем трем элементам объекта dinnerTime типа Time. Если реали­зация struct изменится (например, время теперь будет представляется как
число секунд после полуночи), то все программы, которые используют
struct, нужно будет изменить. Не существует никакого «интерфейса», гарантирую­щего, что программист правильно использует тип данных и что данные яв­ляются непротиворечивыми.
Замечание по технике программирования 6.1
Важно писать программы, которые легко понимать и поддерживать. Изменения яв­ляются скорее правилом, чем исключением. Программисты должны предвидеть, что их коды будут изменяться. Как мы увидим, классы способствуют модифицируемости программ.
// FIG6_1.СРР
// Создание структуры, задание и печать ее элементов. #include «ciostream. h>

struct Time { int hour; int minute; int second;
} ;
//определение структуры // 0-23 // 0-59 // 0-59

// прототип // прототип
void printMilitary(const Time &) void printStandard(const Time S)
main() {

Time dinnerTime;
// переменная нового типа Time

// задание элементам правильных значения dinnerTime.hour = 18; dinnerTime.minute = 30; dinnerTime.second = 0;
cout « "Обед состоится в ";
printMilitary(dinnerTime);
cout « " по военному времени," « endl
<< "что соответствует "; printStandard(dinnerTime);
cout << " по стандартному времени." << endl;
// задание элементам неправильных значений dinnerTime.hour = 2 9; dinnerTime.minute = 73; dinnerTime.second = 103;
cout « endl « "Время с неправильными значениями: "; printMilitary (dinnerTime); cout << endl; return 0;
Рис. 6.1. Создание структуры, задание и печать ее элементов (часть 1 из 2)

// Печать времени в военном формате
void printMilitary(const Time St)
{
cout « (t.hour < 10 ? "0" : "") « t.hour
« ":"« (t.minute < 10 ? "0" : "") « t.minute « ":"« (t.second < 10 ? "0" : "") « t.second;
}
// Печать времени в стандартном формате
void printStandard(const Time &t)
{
cout «  ((t.hour == 0 || t.hour == 12) ? 12 : t.hour %12)
«  ":"« (t.minute < 10 ? "0" : "") « t.minute
«  ":"« (t.second < 10 ? "0" : "") « t.second
«  (t.hour < 12 ? " AM" : " PM");
}
Обед состоится в 18:30:00 по военному времени,
что соответствует 6:30:00 РМ по стандартному времени.
Время с неправильными значениями: 29:73:103
Рис. 6.1. Создание структуры, задание и печать ее элементов (часть 2 из 2)
Существуют и другие проблемы, связанные со структурами в стиле С. В С структуры не могут быть напечатаны как единое целое, только по одному элементу с соответствующим форматированием каждого. Для печати элемен­тов структуры в каком-либо подходящем формате должна быть написана функция. Глава 8, «Перегрузка операций» покажет, как перегрузить опера­цию «, чтобы предоставить возможность простой печати объектов типа структура (С++ расширяет понятие структуры) или типа класс. В С струк­туры нельзя сравнивать в целом; их нужно сравнивать элемент за элементом. Глава 8 покажет, как перегрузить операции проверки равенства и отношения, чтобы можно было в Си++ сравнивать объекты типов структура и класс.
В следующем разделе мы вновь использует нашу структуру Time, но уже как класс, и продемонстрируем некоторые преимущества создания таких так называемых абстрактных типов данных, как классы. Мы увидим, что классы и структуры в С++ можно использовать почти одинаково. Различие между ними состоит в доступности по умолчанию элементов каждого из этих типов. Это будет более детально объяснено позже.

6.5. Использование абстрактного типа данных Time с помощью класса

Классы предоставляют программисту возможность моделировать объек­ты, которые имеют атрибуты (представленные как данные-элементы) и ва­рианты поведения или операции (представленные как функции-элементы). Типы, содержащие данные-элементы и функции-элементы, обычно опреде­ляются в С++ с помощью ключевого слова class.
Функции-элементы иногда в других объектно-ориентированных языках называют методами, они вызываются в ответ на сообщения, посылаемые объекту. Сообщение соответствует вызову функции-элемента.
Когда класс определен, имя класса может быть использовано для объ­явления объекта этого класса. Рис. 6,2 содержит простое определение класса Time.
Определение нашего класса Time начинается с ключевого слова class. Тело определения класса заключается в фигурные скобки ({ }). Определение класса заканчивается точкой с запятой. Определение нашего класса Time, как и нашей структуры Time, содержит три целых элемента hour, minute и second.
Забывается точка с запятой в конце определения класса (или структуры).
class Time { public: Time();
void setTime(int, int, int) ; void printMilitary(); void printStandatd() ; private:
int hour; // 0-23 int minute; // 0 -59 int second; // 0-59 } ;
Рис. 6.2. Простое определение классЗЧте
Остальные части определения класса — новые. Метки public: (открытая) и private: (закрытая ) называются спецификаторами доступа к элементам. Любые данные-элементы и функции-элементы, объявленные после специфи­катора доступа к элементам public: (и до следующего спецификатора доступа к элементам), доступны при любом обращении программы к объекту класса Time. Любые данные-элементы и функции-элементы, объявленные после спе­цификатора доступа к элементам private: (и до следующего спецификатора доступа к элементам), доступны только функциям-элементам этого класса. Спецификаторы доступа к элементам всегда заканчиваются двоеточием (:) и могут появляться в определении класса много раз и в любом порядке. В дальнейшем в тексте нашей книги мы будем использовать записи специфи­каторов доступа к элементам в виде public и private (без двоеточия).
Используйте при определении класса каждый спецификатор доступа к элементам только один раз, что сделает программу более ясной и простой для чтения. Разме­щайте первыми элементы public, являющиеся общедоступными.
Определение класса в нашей программе содержит после спецификатора доступа к элементам public прототипы следующих четырех функций-элемен­тов: Time, setTime, printMilitary и printStandard. Это — открытые функ­ции-элементы или открытый интерфейс услуг класса. Эти функции будут использоваться клиентами класса (т.е. частями программы, играющими роль пользователей) для манипуляций с данными этого класса.
Обратите внимание на функцию-элемент с тем же именем, что и класс. Она называется конструктором этого класса. Конструктор — это специаль­ная функция-элемент, которая инициализирует данные-элементы объекта этого класса. Конструктор класса вызывается автоматически при создании объекта этого класса. Мы увидим, что обычно класс имеет несколько кон­структоров; это достигается посредством перегрузки функции.
После спецификатора доступа к элементам private следуют три целых элемента. Это говорит о том, что эти данные-элементы класса являются до­ступными только функциям-элементам класса и, как мы увидим в следующей главе, «друзьям» класса. Таким образом, данные-элементы могут быть до­ступны только четырем функциям, прототипы которых включены в опреде­ление этого класса (или друзей этого класса). Обычно данные -элементы пере­числяются в части private, а функции-элементы — в части public. Как мы увидим далее, можно иметь функции-элементы private и данные public; пос­леднее не типично и считается в программировании дурным вкусом.
Когда класс определен, его можно использовать в качестве типа в объ­явлениях, например, следующим образом:
Time sunset,             // объект типа Time
arrayOfTimes[5],         // массив объектов типа Time
*pointerToTime,          // указатель на объект типа Time
SdinnerTime = sunset;    //ссылка на объект типа Time
Имя класса становится новым спецификатором типа. Может существо­вать множество объектов класса как и множество переменных типа, напри­мер, такого, как int. Программист по мере необходимости может создавать новые типы классов. Это одна из многих причин, по которым С++ является расширяемым языком.
Программа на рис. 6.3 использует класс Time. Эта программа создает единственный объект класса Time, названный t. Когда объект создается, авто­матически вызывается конструктор Time, который явно присваивает нулевые начальные значения всем данным-элементам закрытой части private. Затем печатается время в военном и стандартном форматах, чтобы подтвердить, что элементы получили правильные начальные значения. После этого с по­мощью функции-элемента setTime устанавливается время и оно снова печа­тается в обоих форматах. Затем функция-элемент setTime пытается дать дан­ным-элементам неправильные значения и время снова печатается в обоих форматах.
Снова отметим, что данные-элементы hour, minute и second предваряются спецификатором доступа к элементам private. Эти закрытые данные-элемен­ты класса обычно недоступны вне класса (но, как мы увидим в главе 7, друзья класса могут иметь доступ к закрытым элементам класса.) Глубокий смысл такого подхода заключается в том, что истинное представление данных внутри класса не касается клиентов класса. Например, было бы вполне воз­можно изменить внутреннее представление и представлять, например, время внутри класса как число секунд после полуночи. Клиенты могли бы исполь­зовать те же самые открытые функции-элементы и получать те же самые результаты, даже не осознавая произведенных изменений. В этом смысле, говорят, что реализация класса скрыта от клиентов. Такое скрытие инфор­мации способствует модифицируемости программ и упрощает восприятие класса клиентами.

II FIG6_3.CPP // Класс Time, iinclude

// Определение абстрактного типа данных (АТД) Time class Tlme{ public:
Timet);              // конструктор
void setTime(int, int, int)
// установка часов, минут //и секунд
void printMilitary(); void printStandard();
// времени в военном формате
// печать времени
//в стандартном формате
private:
int
hour;
//
0 -
- 23
int
minute;
//
0 -
- 59
int
second;
II
0 -
- 59

// Конструктор Time присваивает нулевые начальные значения // каждому элементу данных. Обеспечивает согласованное // начальное состояние всех объектов Time Time::Time О     { hour = minute = second =0; }
II Задание нового значения Time в виде военного времени. // Проверка правильности значений данных. // Обнуление неверных значений, void Time::setTime(int h, int m, int s)
{
hour = (h >= 0 && h < 24) ? h : 0; minute = (m >= 0 && m < 60) ? m : 0; second = (s >= 0 && s < 60) ?s : 0;
}
// Печать времени в военном формате
void Time:: printMilitary ( ) {
cout « (hour < 10 ? "0" :"") « hour« ":"
« (minute < 10 ? "0": "") « minute « ":" « (second < 10 ? "0" : "") « second;
}
// Печать времени в стандартном формате
void Time:: printStandard() {
cout « ( (hour == 0 || hour ==12) ? 12 : hour % 12) «":" « (minute < 10 ? "0": "") « minute <<":" « (second < 10 ? "0" : "") « second « (hour < 12 ? " AM" : " PM");
}
Рис. 6.3. Использование абстрактного типа данных Time как класса (часть 1 из 2)

II Формирование проверки простого класса Time
main() {
Time t;     // определение экземпляра объекта t класса Time
cout « "Начальное значение военного времени равно "; t.printMilitary(); cout << endl
<< "Начальное значение стандартного времени равно "; t.printStandard();
t.setTime(13, 27, 6);
cout « endl << endl << "Военное время после setTime равно ' t.printMilitary();
Л
cout << endl << "Стандартное время после setTime равно "; t.printStandard();
t.setTime(99, 99, 99);      // попытка установить
// неправильные значения
cout << endl << endl
<< "После попытки неправильной установки: " « endl « "Военное время: "; t.printMilitary();
cout << endl << "Стандартное время: "; t.printStandard(); cout << endl; return 0;
}
Начальное значение военного времени равно 00:00:00 Начальное значение стандартного времени равно 12:00:00 AM
Военное время после setTime равно 13:27:06 Стандартное время после setTime равно 1:27:06 РМ
После попытки неправильной установки: Военное время: 00:00:00 Стандартное время: 12:00:00 AM
Рис. 6.3. Использование абстрактного типа данных Time как класса (часть 2 из 2)

Замечание по технике программирования 6.2
Клиенты класса используют класс, не зная внутренних деталей его реализации. Если реализация класса изменяется (например, с целью улучшения производительности), интерфейс класса остается неизменным и исходный код клиента класса не требует изменений. Это значительно упрощает модификацию систем.
В нашей программе конструктор Time просто присваивает начальные зна­чение, равные 0, данным-элементам, (т.е. задает военное время, эквивалент­ное 12 AM). Это гарантирует, что объект при его создании находится в из­вестном состоянии. Неправильные значения не могут храниться в данных-элементах объекта типа Time, поскольку конструктор автоматически вызывается при создании объекта типа Time, а все последующие попытки изменить данные-элементы тщательно рассматриваются функцией setTime.

Замечание по технике программирования 6.3
Функции-элементы обычно короче, чем обычные функции в программах без объ­ектной ориентации, потому что достоверность данных, хранимых в данных-элементах, идеально проверена конструктором и функциями-элементами, которые сохраняют новые данные.
Отметим, что данные-элементы класса не могут получать начальные зна­чения в теле класса, где они объявляются. Эти данные-элементы должны получать начальные значения с помощью конструктора класса или им можно присваивать значения через функции.
Типичная ошибка программирования 6.2
Попытка явно присвоить начальное значение данным-элементам в определении класса.
Функция с тем же именем, что и класс, но со стоящим перед ней сим­волом тильда (~), называется деструктором этого класса (наш пример не включает деструктор). Деструктор производит «завершающие служебные дей­ствия» над каждым объектом класса перед тем, как память, отведенная под этот объект, будет повторно использована системой. Подробнее мы обсудим конструкторы и деструкторы позже в этой главе и в главе 7.
Заметим, что функции, которыми класс снабжает внешний мир, пред­варяются меткой public. Открытые функции реализуют все возможности класса, необходимые для его клиентов. Открытые функции класса называют интерфейсом класса или открытым интерфейсом.
Замечание по технике программирования 6.4
Клиенты имеют доступ к интерфейсу класса, но не имеют доступа к реализации класса.
Объявление класса содержит объявления данных-элементов и функций- элементов класса. Объявления функций-элементов являются прототипами функций, которые мы обсуждали в предыдущих главах. Функции-элементы могут быть описаны внутри класса, но хороший стиль программирования заключается в описании функций вне определения класса.
Замечание по технике программирования 6.5
Объявление функций-элементов внутри определения класса и описание функций- элементов, вне этого определения отделяет интерфейс класса от его реализации. Это способствует высокому качеству разработки программного обеспечения.

Отметим использование бинарной операции разрешения области дейст­вия (::) в каждом определении функции-элемента, следующем за определе­нием класса на рис. 6.3. После того, как класс определен и его функции- элементы объявлены, эти функции-элементы должны быть описаны. Каждая функция-элемент может быть описана прямо в теле класса (вместо включение прототипа функции класса) или после тела класса. Когда функция-элемент описывается после соответствующего определения класса, имя функции пред­варяется именем класса и бинарной операцией разрешения области дейст- вия (::).Поскольку разные классы могут иметь элементы с одинаковыми име­нами, операция разрешения области действия «привязывает» имя элемента к имени класса, чтобы однозначно идентифицировать функции-элементы дан­ного класса.
Несмотря на то, что функция-элемент, объявленная в определении класса, может быть описана вне этого определения, эта функция-элемент все равно имеет областью действия класс, т.е. ее имя известно только другим эле­ментам класса пока к ней обращаются посредством объекта класса, ссылки на объект класса или указателя на объект класса. Об области действия класса мы более подробно еще поговорим позднее.
Если функция-элемент описана в определении класса, она автоматически встраивается inline. Функция-элемент, описанная вне определения класса, может быть сделана встраиваемой посредством явного использования клю­чевого слова inline. Напомним, что компилятор резервирует за собой право не встраивать никаких функций.
Совет по повышению эффективности 6.3
Описание небольших функций-элементов внутри определения класса автоматически встраивает функцию-элемент inline (если компилятор решит делать это). Это может улучшить производительность, но не способствует улучшению качества проектиро­вания программного обеспечения.
Интересно, что функции-элементы printMilitary и printStandard не по­лучают никаких аргументов. Это происходит потому, что функции-элементы неявно знают, что они печатают данные-элементы определенного объекта типа Time, для которого они активизированы. Это делает вызовы функций-эле­ментов более краткими, чем соответствующие вызовы функций в процедур­ном программировании. Это уменьшает также вероятность передачи неверных аргументов, неверных типов аргументов или неверного количества аргумен­тов.
Замечание по технике программирования 6.6
Использование принципов объектно-ориентированного программирования часто может упростить вызовы функции за счет уменьшения числа передаваемых пара­метров. Это достоинство объектно-ориентированного программирования проистекает из того факта, что инкапсуляция данных-элементов и функций-элементов внутри объекта дает функциям-элементам право прямого доступа к данным-элементам.
Классы упрощают программирование, потому что клиент (или пользова­тель объекта класса) имеет дело только с операциями, инкапсулированными или встроенными в объект. Такие операции обычно проектируются ориен­тированными именно на клиента, а не на удобную реализацию. Клиентам нет необходимости касаться реализации класса. Интерфейсы меняются, но не так часто, как реализации. При изменении реализации соответственно должны изменяться ориентированные на реализацию коды. Л путем скрытия реализации мы исключаем возможность для других частей программы ока­заться зависимыми от особенностей реализации класса.
Часто классы не создаются «на пустом месте». Обычно они являются производными от других классов, обеспечивающих новые классы необходи­мыми им операциями. Или классы могут включать объекты других классов как элементы. Такое повторное использование программного обеспечения зна­чительно увеличивает производительность программиста. Создание новых классов на основе уже существующих классов называется наследованием и подробно обсуждается в главе 9. Включение классов как элементов других классов называется композицией и обсуждается в главе 7.