Программирование >>  Структурное программирование 

1 ... 186 187 188 [ 189 ] 190 191 192 ... 342


Заметим, что перегруженная функция-операция поместить в поток класса Circle способна ссылаться на х и у непосредственно, потому что они являются защищенными элементами базового класса Point. Заметим также, что ссылаться на X и у необходимо через объекты, например, с.х и су. Это объясняется тем, что перегруженная функция-операция поместить в поток является не элементом класса Circle, а другом этого класса.

Программа дрансвер (рис. 9.4, часть 5) создает pointPtr - указатель на объект класса Point, и создает экземпляр класса Point - объект р. Затем создается circlePtr - указатель на объект класса Circle, и создается с - объект класса Circle. Объекты рис выводятся с помощью перегруженных операций поместить в поток, чтобы показать, что они инициализированы правильно.

Далее драйвер демонстрирует присваивание указателя производного класса (адреса объекта с) указателю базового класса pointPtr и выводит объект с класса Circle, используя перегруженную операцию поместить в поток для класса Point и разыменование указателя *pointPtr. Отметим, что отображается только часть объекта с класса Circle, относящаяся к классу Point. Присваивание указателя производного класса указателю базового класса всегда выполняется правильно, поскольку объект производного класса является в то же самое время объектом базового класса. Указатель базового класса видит только часть производного класса, относящуюся к базовому классу. Компилятор выполняет неявное преобразование указателя производного класса в указатель базового класса.

Далее драйвер демонстрирует присваивание указателя производного класса (адреса объекта с) указателю базового класса pointPtr и затем приведение типа pointPtr обратно к Circle *. Результат этой операции приведения типа присваивается circlePtr. Объект с класса Circle выводится, с помощью перегруженной операции поместить в поток класса Circle и разыменования указателя *circlePtr. Площадь объекта с класса Circle выводится через circlePtr. Результат содержит правильное значение площади, потому что эти зазатели всегда указывают на объект класса Circle.

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

Далее драйвер демонстрирует присваивание указателя базового класса (адреса объекта р) указателю базового класса pointPtr и обратное приведение pointPtr к типу Circle*. Результат операции приведения типа присваивается указателю circlePtr. Объект р класса Point выводится с помощью перегруженной операции поместить в поток для класса Circle и разыменования указателя *\circlePtr. Отметим странное выходное значение для элемента радиус. Выведение объекта класса Point как Circle дает в результате неправильное значение для radius, потому что указатель указывает на объект класса Point. А объект класса Point не имеет элемента radius. Поэтому программа выводит некоторое значение, оказавшееся в памяти в той области, в которой circlePtr ожидает элемент данных radius. Площадь объекта, на который указывает circlePtr (объект р класса Point), также выводится через circlePtr. Отметим,



что значение площади равно 0.00, потому что ее вычисление основано на несуществующем значении radius. Очевидно, что доступ к данным-элементам, которых в действительности нет, в данном случае не опасен. Но вызов несуществующих функций-элементов может вызвать необратимую ошибку в программе.

9.5. Использование функций-элементов

Функции-элементы производного класса могут нуждаться в доступе к определенным функциям-элементам и данным-элементам базового класса.

Замечание по технике программирования 9.1

Производный класс не имеет прямого доступа к закрытым элементам своего базового класса.

Это решающий аспект разработки программного обеспечения на С++. Если бы закрытые элементы базового класса были доступны производному классу, это разрушило бы инкапсуляцию базового класса. Недоступность закрытых элементов - огромное подспорье в тестировании, отладке и правильной модификации систем. Если бы закрытые элементы базового класса были доступны производному классу, то доступ к ним стал бы возможным для классов, порожденных этим производным классом и так далее. Это передавало бы по наследству доступ к тому, что по замыслу должно быть закрытыми данными, и выгоды инкапсуляции были бы потеряны из-за иерархии классов.

9.6. Переопределение элементов базового класса в производном классе

Производный класс может переопределить функцию-элемент базового класса. При описании в производном классе функции с тем же именем, версия функции производного класса переопределяет версию базового класса. Чтобы сделать доступной для производного класса версию функции базового класса, нужно использовать операцию разрешения области действия.

Типичная ошибка программирования 9.3

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

Замечание по технике программирования 9.2

При переопределении в производном классе функции-элемента базового класса нет необходимости получать такую же сигнатуру, как у функции-элемента базового класса.



EMPLOY.Н

Определение класса Employee #ifndef EMPLOY H ttdefine EMPLOY H

class Employee { public I

Employee(const char*, const char*); конструктор void print 0 const; вывод имени и фамилии

-Employee О; деструктор

private:

char *firstName; динамически размещенная строка

char *lastName; динамически размещенная строка

#endif

Рассмотрим упрощенный класс Employee (служащие). Он хранит имена firstName и фамилии lastName служащих. Эта информация - общая для всех служащих, включая находящихся в классах, порожденных из класса Employee. На основе класса Employee порождены классы HourlyWorker (работники с почасовой оплатой). Pieceworker (работники со сдельной оплатой), Boss (управляющие) и CommissionWorker (работники с комиссионной оплатой). HourlyWorker получают почасовую оплату из расчета 40 часов в неделю и полуторную почасовую оплату за работу сверх 40 часов. Pieceworker работают сдельно и нолучают фиксированную плату за единицу произведенной продукции (для простоты допустим, что эти работники делают только один тип продукции, так что закрытыми данными-элементами являются количество произведенных единиц продукции и ставка за единицу продукции). Boss получает фиксированную зарплату за неделю. ComissionWorker получает маленькую еженедельную фиксированную основную зарплату плюс процент от своих оптовых продаж за неделю. Для простоты мы изучим только класс Employee и производный класс HourlyWorker.

Наш следующий пример показан на рис. 9.5, части с 1 по 5. Части 1 и 2 показывают определение класса Employee и определения функций-элементов Employee. Части 3 и 4 показывают определение класса HourlyWorker и определение функции-элемента HourlyWorker. Часть 5 показывает программу драйвер для иерархии наследования Employee - HourlyWorker, которая просто создает объект HourlyWorker, задает ему начальные значения и вызывает функцию- элемент print класса HourlyWorker для вывода данных объекта.

Определение класса Employee (рис. 9.5, часть!) содержит два закрытых элемента данных типа char* - firstName и lastName, и три функции-элемента: конструктор, деструктор и print. Функция конструктор (рис. 9.5, часть 2) получает две строки и динамически выделяет память для их хранения. Заметим, что макрос assert (обсуждается в главе 18 Другие темы ) используется для того, чтобы определить, была ли выделена память для firstName и lastName. Если нет, программа завершается сообщением об ошибке, указывающим проверенное условие, номер строки, в которой появилось условие, и файл, в котором локализовано условие.



1 ... 186 187 188 [ 189 ] 190 191 192 ... 342

© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки.
Яндекс.Метрика