Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Глава 20. Статические члены 221




Сумма по сберегательным счетам 355 Общая сумма = 1114.8

Рассмотрим каждую из функций-членов, начиная  с класса                                   Конструктор присваивает счету его номер. Значение по умолчанию "= 0" позволяет программе создавать объект с номером счета по умолчанию, равным нулю.

Checking = new Checking c2 = new

В данном случае объект       класса                  создается с номером счета, равным тогда как объект с2 создается с номером счета по умолчанию, который равен нулю.

Функции                             и acntBalance () предоставляют внешнему миру доступ к защищенным  членам                  и balance. Задачей этих функций является предоставление внешним функциям — не членам значений, изменить которые невоз- можно. Кроме того, эти функции, обеспечивающие доступ к членам, предохраняют внешние функции от необходимости внесения изменений при переменах в методе хранения номера счета или баланса.

Функции                  ()  и                       () отвечают за вложение и снятие денег со счета.  Поскольку функция () довольно проста, она была определена как inline-функция. Функция withdrawal (), будучи несколько сложнее, объявлена в классе, но определяется позже.

Функция displa y (} выводит важные данные на устройство стандартного вывода. Класс Savings, в сущности, идентичен классу checking, за исключением допол-

нительного члена                                   который используется для отслеживания количест- ва проведенных снятий.

Место под объекты сберегательного и чекового счета выделяется в массивах svgAcnts и         Максимальное количество счетов определено величиной

Функция           () несколько сложнее своей сестры из

скольку она имеет дело с двумя разными типами счетов. После проверки ввода на ра- венство         функция () использует конструкцию switch, чтобы выбрать тип счета: с для чекового и S для сберегательного. Конструкция switch использована в этой программе по двум причинам: во-первых, ее проще расширить, добавляя к ней дополнительные варианты; во-вторых, она предоставляет вариант defaul t (вариант по умолчанию) для обработки неверного ввода.

Как и ранее, вторая часть  функции               О обеспечивает отображение информа- ции о счете, собранной в первой части этой функции.

Обратите внимание на то, как содержимое классов Checking и Savings скрыто от main (). Так, например, main () просит объект показать свое содержимое, однако при этом не имеет никакого представления о том, как класс выбирает, что именно и как это показать.

Функция process (), которая обрабатывает текущие вложения и снятия, полагается на функции-члены deposi t () и withdrawal (), которые выполняют за нее всю черную ра- боту. Хотя вы и знаете, как именно выполняются эти действия, помните, что process () об этом не имеет никакого понятия. Работа счета касается только самого класса счета.

Советую пройти эту программу в пошаговом режиме. Ничто иное не даст более полного представления о программе, чем ее рассмотрение в действии.

Хотите — верьте, хотите — нет, но с позиции программирования Budget2 разрабатыва- ется  легче,  чем  1. Когда я писал класс Savings, я не должен был волноваться о том, как он будет использоваться главной программой (то же относится и к классу Checking). Когда же я работал над функцией main (), то не думал о содержимом класса.

Однако в этой программе есть один небольшой недостаток: классы Savings и Checking имеют очень много общего, и хотелось бы найти возможность уменьшить ко- личество повторений кода. Эта возможность и является темой части 4,

в конце которой вы найдете очередную версию нашей программы — Budget3.

 

222                    Часть "Классическое" программирование


ЧастьIV

 



Наследование


Издискуссии по вопросамобъектно-ориентированнойфилософии в части 3 становится ясно, что в реальном мире существует две вещи, которые нельзя выразить

спомощьюфункционально-ориентированных

Первое — это возможность                с отдельными объектами.

Я привел пример использования микроволновой пени

для приготовления закуски. Она предоставляет интерфейс (на лицевой панели), который я использую для управления, совершенно не вникая в подробности работы печи.

Я буду вести себя точно так же, даже если буду знать все о том, как именно она устроена (хотя я этого не знаю).

Второй аспект реального мира, закрытый для функциональных программ, —

это классификация объектов: распознавание и использование их подобия.

Если в рецепте приготовления того или иного блюда указана печь любого типа, то, работая с микроволновой печью,

я буду уверен, что использую правильное устройство, поскольку микроволновая пень является      из типов печей.

В предыдущей части вы познакомились с механизмом, используемым в         для осуществления

первойвозможностиобъектно-ориентированного программирования, — с классами.

Для обеспечения второй возможности C++ использует

концепцию, называемую наследованием,

которая расширяет понятие и возможности классов. Именно о наследовании и пойдет речь в этой части книги.


Глава21



Наследование классов

Зачем нужно наследование Как наследуется класс Конструирование подкласса Отношение СОДЕРЖИТ

 

этой главе обсуждается наследование (inheritance), т.е. способность одного класса наследовать возможности или свойства другого класса.

Наследование это общепринятая концепция. Я — человек (за исключением раннего утра...). И я наследую некоторые свойства класса Человек, например воз- можность говорить (в большей или меньшей степени), интеллект (надеюсь, что в большей степени), необходимость в воздухе, воде, пище и всяких витаминах. Эти свойства не являются уникальными для каждого отдельного человека. Очевидно, что класс Человек наследует зависимость от воды, воздуха и пищи у класса Млекопитаю- щие, который, в свою очередь, наследует эти свойства у класса Животные.

Концепция, в основе которой лежит способность передавать свойства по наследст- ву, очень мошная. Благодаря ей можно значительно сэкономить место при описании реального объекта. Например, если мой сын спросит:                                                                     такое утка?", я смогу ска- зать: "Это птица,  которая        Несмотря на краткость, этот ответ несет в себе всю необходимую для описания утки (по крайней мере, для моего сына) информа- цию. Мой сын знает, что такое птица, и может понять, что утке присущи все свойства птицы плюс свойство "кряканье".

В объектно-ориентированных языках такая наследственная связь выражается в возможности одного класса наследовать другой. Таким образом, объектно- ориентированные языки позволяют создавать модели, более близкие к реальному ми- ру (а именно для этого они и созданы), чем модели, построенные с помощью языков, не поддерживающих наследование.

В C++ один класс может наследовать другой следующим образом:

class Student

 

:  Student

 

} ;

В этом примере GraduateStudent наследует все члены класса Student. Таким

образом, GraduateStudent ЯВЛЯЕТСЯ студентом. Конечно, при этом GraduateStu- dent может также содержать уникальные, присущие именно ему члены.

 

нужно наследование

Наследование      включено в C++ по нескольким причинам. Конечно, основной из них была необходимость выражать связи между классами с помощью наследования (к этому я еще вернусь). Менее важной целью было уменьшение размера исходного

Глава 21. Наследование классов                                                     225


кода. Представьте себе, что у вас есть класс Student и вас попросили добавить новый класс под названием GraduateStudent. В этом случае наследование значительно уменьшит количество членов, которые вам придется добавлять в класс. Все, что вам действительно нужно в классе GraduateStudent, — это члены, которые будут описы- вать отличия между студентами и аспирантами.

С этим связана и более важная проблема — необходимость механизма повторного использования. Разработчики программного обеспечения на каком-то этапе поняли, что глупо начинать каждый новый проект с нуля, заново создавая одни и те же про- граммные компоненты.

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

К сожалению, такая простая философия слишком мало практикуется в области программного обеспечения. За исключением ряда мелких функций, входящих в со- став стандартных библиотек С, редко встретишь повторное использование программ- ных компонентов. Одной из проблем, связанных с повторным использованием, явля- ется почти полная невозможность отыскать в готовой программе компонент, который бы отвечал всем вашим требованиям. Обычно все уже существующие компоненты приходится дописывать и переписывать перед применением в данном приложении (как если бы работа по созданию самолета состояла в том, чтобы убрать с помощью напильника все лишнее у паровоза...).

В программировании есть правило: "Если ты что-то открыл, значит, ты это уже сломал". Другими словами, если вам пришлось переписать функцию или класс, чтобы приспособить ее к новому приложению, то вам придется заново протестировать всю функцию, а не только те части, которые вы изменили.  Изменения могут внести ошибки в любое место существующего кода.

Наследование позволяет приспосабливать существующие классы к новым прило- жениям, не внося изменений в их внутреннее устройство. Существующий класс на- следуется новым подклассом, который и будет содержать все необходимые добавления и изменения.

Это приводит нас к третьему преимуществу наследования. Представьте себе, что вы наследуете некий существующий класс. Затем вы находите, что базовый класс содержит ошибку, которую нужно исправить. Если бы вы переделывали класс для его повторного использования, вам бы пришлось вручную проверить новый класс, отыскивая ошибку в каждом отдельно взятом приложении. Однако если вы насле- довали класс без изменений, то можете без особых хлопот заменить старую версию класса новой.

Это потрясающе

Люди составляют обширные системы, чтобы было проще разбираться в том, что их окружает. Тузик является частным случаем собаки, которая является частным слу- чаем собакообразных, которые входят в состав млекопитающих, и т.д. Так легче познавать мир.

Если использовать другой пример, можно сказать, что студент является человеком (точнее, его частным случаем). Как только это сказано, я уже знаю довольно много о студентах (об американских студентах, естественно). Я знаю, что они имеют но- мера социального страхования, что они слишком много смотрят телевизор и по- стоянно мечтают о сексе. Я знаю все это потому, что это свойства всех людей.

 

 

226                                                                    Часть IV. Наследование


В C++ мы говорим, что класс                 наследует класс Person. Кроме того, мы говорим, что Person является базовым классом для класса student . Наконец, мы говорим, что studen t ЯВЛЯЕТСЯ Person (использование прописных букв — щепринятый метод отражения уникального типа связи; не я это придумал). Эта терминология используется в C++ и других объектно-ориентированных языках программирования.

Заметьте,  что  хотя             и ЯВЛЯЕТСЯ Person, обратное не верно. Person не ЯВЛЯЕТСЯ Student (такое выражение следует трактовать в общем смысле, поскольку конкретный человек, конечно же, может оказаться студентом). Существует много лю- дей, которые являются членами класса Person и не являются членами класса

Кроме того, класс student имеет средний балл, a Person его не имеет.

Свойство наследования транзитивно. Например, если я определю новый класс GraduateStudent как  подкласс  класса                                                               то он тоже будет наследником Person. Это значит, что будет выполняться следующее: если GraduateStudent ЯВЛЯЕТСЯ Student и Student ЯВЛЯЕТСЯ Person, то GraduateStudent ЯВЛЯЕТСЯ Person.

 

класс

Здесь приведен пример уже рассмотренного класса GraduateStudent, который дополнен несколькими членами.

<string.h>

class

 

classStudent

{

 


 

{

{
voidaverage  =  0.0 ;=


= "no name")

 

hours; * +


}

 

protcehcatred:


=average/

)){{rreettuurrnn


); ifnltoataverage;

classGraduateStudent:publicStudent

Глава 21. Наследование классов                                                                 227


int       > { return

 

protected:

Advisor advisor; int

 

int    )

{

Student       Lee


 

 

return 0;

}


 

2.5);

3.0);


В этом примере класс Student содержит тс же члены, что и ранее. Объект — просто один из объектов Student. Класс                                                                                  несколько отличается по структуре от класса Student; двоеточие и следующее за ним publi c                                                             объяв- ляет класс  GraduateStudent Student.

Ключевое слово publi c наводит на мысль о том, что может существовать и защи- щенное наследование, однако его обсуждение выходит за рамки нашей книги (вы

не забыли этот термин?).

Объект gs как член подкласса                     может делать то же, что и объект             Он содержит  данные-члены  name,                                                                                                           average и

addCourse (). Кроме того, GraduateStuder.t содержит также  члены                                  (), adviso r и quali f ierGrade- Таким образом, gs в прямом смысле этого слова ЯВЛЯЕТСЯ классом Student плюс кое-что еше.

Разберем                          ниже сценарий.

void        s)

{

//    что угодно...

 

intmain()

{

GraduateStudent

;

return

}

Обратите внимание,  что  функция                  принимает в качестве аргумента ссылку

на объект класса student .  Однако  в                   () мы  передаем  ей                    класса GraduateStudent . Функция спокойно принимает этот аргумент именно потому, что (а ну-ка еше раз, все вместе!)                  ЯВЛЯЕТСЯ Student" .

Это же условие позволяет вызывать функцию-член класса Student из объекта класса GraduateStudent, как это делается в приведенном ниже примере.

in t              )

[

gs ;

//вызываем функцию

(3, 2.5) ;

return

 

228                 Часть IV. Наследование


Хотя подкласс и имеет доступ к защищенным членам базового класса, а значит, может инициализировать их, было бы хорошо, если бы базовый класс все же конст- руировал сам себя. В действительности так и происходит.

Перед тем как управление получает код, стоящий за открывающей фигурной скоб- кой класса GraduateStudent, оно передается конструктору по умолчанию класса (поскольку другой конструктор не был указан). Если бы класс studen t был наследником другого класса, например Person, то конструктор этого  класса вызывал- ся бы перед передачей управления конструктору  student .  Подобно                                                                                                                                             объ- ект строится, начиная с "фундаментального" уровня в соответствии со структурой на-

следования классов и вызывая конструкторы всех классов, составляющих данный.

Как и в случае с объектами-членами, вам может понадобиться передавать аргументы конструктору базового класса. Это делается почти так же, как и изученная ранее переда- ча аргументов конструктору объекта-члена (смотрите приведенный ниже пример).

class GraduateStudent : public Student

{

public:

Advisors adv) :

 

{

0;

}

//все ос   такое же, как и раньше

};

void

{

Kay Doodle", advisor);

//               остальное, что должна                           эта функция...

}

В ЭТОМпримере конструктор класса GraduateStudent вызывает конструктор Stu-

dent, передавая ему аргумент                 Базовый класс конструируется до любых объек- тов-членов, а значит, конструктор класса Student вызывается перед конструктором Advisor. И только после конструктора Advisor (который вызывается для члена ad- visor ) начинает работу конструктор GraduateStudent.

Следуя правилу о том, что деструкторы вызываются в порядке, обратном вызову конструкторов, первым вызывается                                                                                         GraduateStudent. После того как он свою работу, управление передается деструктору класса Advisor, а затем деструктору Student. Если бы studen t был наследником класса Person, его деструк-

тор получил бы управление после деструктора

И это логично. Блок памяти                    преобразуется в объект student , а уже затем конструктор для GraduateStudent превращает этого студента в аспиранта. Деструк- тор же просто выполняет этот процесс в обратном направлении.

 

Обратите внимание, что класс GraduateStudent включает в себя члены классов Student и Advisor, однако он включает их по-разному. Определяя данные-члены класса  Advisor,  вы        что класс Student содержит внутри все данные-члены класса Advisor, но вы не можете сказать, что GraduateStudent ЯВЛЯЕТСЯ Advi-


Поделиться:



Последнее изменение этой страницы: 2019-04-19; Просмотров: 182; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.068 с.)
Главная | Случайная страница | Обратная связь