246071-Либерти-Освой-самостоятельно-С-за-21-день (852741), страница 72
Текст из файла (страница 72)
Связанныйсписок — это динамическая структура данных вроде массива, за исключением того, что всписок можно добавлять произвольное число объектов указанного типа и удалять любой извведенныхобъектов.Данный связанный список разработан для хранения объектов класса Part, где Part —абстрактный тип данных, служащий базовым классом для любого объекта с заданнойпеременной-членом itsPartNumber.
В программе от класса Part производятся два подкласса CarPartHAirPlanePart.Класс Part описывается в строках 26—36, где задаются одна переменная-член и несколькометодов доступа. Предполагается, что затем в объекты класса будет добавлена другая ценнаяинформация и возможность контроля за числом созданных объектов в базе данных. Класс Partописывается как абстрактный тип данных, на что указывает чистая виртуальная функцияDisplay().Обратите внимание, что в строках 40-43 определяется выполнение чистой виртуальнойфункцииDisplay().Предполагается,чтометодDisplay()будетзамещатьсявкаждомпроизводномклассе, но в определении замещенного варианта допускается просто вызывать стандартныйметодизбазовогокласса.Двапростыхпроизводныхкласса,CarPartиAirPlanePart,описываютсявстроках47—59и69—87 соответственно.
В каждом из них замещается метод Display() простым обращением кметодуDisplay()базовогокласса.Класс PartNode выступает в качестве интерфейса между классами Part и PartList. ОнсодержитуказательнаPartиуказательнаследующийузелвсписке.Методыэтогоклассатольковозвращают и устанавливают следующий узел в списке и возвращают соответствующий объектPart.За "интеллектуальность" связанного списка полностью отвечает класс PartsList,описываемый в строках 132—152. Этот класс содержит указатель на головной узел списка(pHead) и, с его помощью продвигаясь по списку, получает доступ ко всем другим методам.Продвижениепоспискуозначаетзапрашиваниетекущегоузлаобадресеследующеговплотьдообнаруженияузла,указателькоторогонаследующийузелравенNULL.Безусловно, в этом примере представлен упрощенный вид связанного списка.
В реальноиспользуемой программе список должен обеспечивать еще больший доступ к первому ипоследнему узлам списка или создавать специальный объект итерации, с помощью которогоклиентысмогутлегкопродвигатьсяпосписку.В то же время класс PartsList предлагает ряд интересных методов, упорядоченных поалфавиту. Зачастую такой подход весьма эффективен, поскольку упрощает поиск нужныхфункций.Функция Find() принимает в качестве параметров PartNumber и значение int.
Если найденразделсуказаннымзначениемPartNumber,функциявозвращаетуказательнаPartипорядковыйномер этого раздела в списке. Если же раздел с номером PartNumber не обнаружен, функциявозвращаетзначениеNULL.Функция GetCount() проходит по всем узлам списка и возвращает количество объектов всписке.ВPartsListэтозначениезаписываетсявпеременную-членitsCount,хотяегоможнолегковычислить,последовательнопродвигаясьnoсписку.Функция GetFirst() возвращает указатель на первый объект Part в списке или значениеNULL,еслисписокпустой.Функция GetGlobalPartsList() возвращает ссылку на статическую переменную-членGiobalPartsList.ОписаниестатическойпеременнойGlobaiPartsListявляетсятипичнымрешениемдля классов типа PartsList, хотя, безусловно, могут использоваться и другие имена.
Взаконченном виде реализация этой идеи состоит в автоматическом изменении конструктораклассаPartтакимобразом,чтобыкаждомуновомуобъектуклассаприсваивалсяномерсучетомтекущегозначениястатическойпеременнойGiobalPartsList.Функция Insert принимает значение указателя на объект Part, создает для него PartNode идобавляетобъектPartвсписоквпорядкевозрастанияномеровPartNumber.Функция Iterate принимает указатель на константную функцию-член класса Part безпараметров, которая возвращает void.
Эта функция вызывается для каждого объекта Part всписке. В описании класса Part таким характеристикам соответствует единственная функцияDisplay(), замещенная во всех производных классах. Таким образом, будет вызываться вариантметодаDisplay(),соответствующийтипуобъектаPart.Функция Operator[] позволяет получить прямой доступ к объекту Part по заданномусмещению. Этот метод обеспечивает простейший способ определения границ списка: еслисписокнулевой,илизаданноесмещениебольшечислаобъектоввсписке,возвращаетсязначениеNULL,сигнализирующееобошибке.В реальной программе имело бы смысл все эти комментарии с описанием назначенийфункцийпривестивописаниикласса.Телофункцииmain()представленовстроках266-303.Встроке268описываетсяссылканаPartsListиинициализируетсязначениемGiobalPartsList.Обратитевнимание,чтоGiobalPartsListинициализируется в строке 154.
Эта строка необходима, поскольку описание статическойпеременной-члена не сопровождается ее автоматическим определением. Поэтому определениестатическойпеременной-членадолжновыполнятьсязапределамиописаниякласса.Встроках274—299пользователюпредлагаетсяуказать,вводитсялидетальдлямашиныилидля самолета. В зависимости от выбора, запрашиваются дополнительные сведения и создаетсяновыйобъект,которыйдобавляетсявсписоквстроке298.Выполнение метода Insert() класса PartList показано в строках 218—264. При вводеидентификационного номера первой детали — 2837 — создается объект CarPart, которыйпередаетсявLinkedList::Insert()cвведенныминомеромдеталиигодомсоздания90.В строке 220 создается новый объект PartNode, принимающий значения новой детали.ПеременнаяNewинициализируетсяномеромдетали.Переменная-членitsCountклассаPartsListувеличиваетсянаединицувстроке226.В строке 228 проверяется равенство указателя pHead значению NULL.
В данном случаевозвращается значение TRUE, поскольку это первый узел списка и указатель pHead в немнулевой.Врезультатевстроке230указателюpHeadприсваиваетсяадресновогоузлаифункциявозвращается.Пользователюпредлагаетсяввестиследующуюдеталь.Внашемпримеревводитсядетальотсамолета с идентификационным номером 37 и номером двигателя 4938. Снова вызываетсяфункция PartsList::Insert() и pNode инициализируется новым узлом.
Статическая переменнаячленitsCountстановитсяравной2ивновьпроверяетсяpHead.ПосколькутеперьpHeadнеравеннулю,тозначениеуказателябольшенеизменяется.В строке 236 номер детали, указанный в головном узле, на который ссылается pHead (внашем случае это 2837), сравнивается с номером новой детали — 378. Поскольку последнийномерменьше,условноевыражениевстроке236возвращаетTRUEиголовнымузломвспискестановитсяновыйобъект.Строкой 238 указателю pNode присваивается адрес того узла, на который ссылалсяуказательpHead.Обратитевнимание,чтовследующийузелспискапередаетсяненовыйобъект,атот,которыйбылвведенранее.Встроке239указателюpHeadприсваиваетсяадресновогоузла.На третьем цикле пользователь вводит деталь для автомобиля под номером 4499 с годомвыпуска 94.
Происходит очередное приращение счетчика и сравнивается номер текущегообъекта с объектом головного узла. В этот раз новый введенный идентификационный номердетали оказывается больше номера объекта, определяемого в pHead, поэтому запускается циклforвстроке243.Значение идентификационного номера головного узла равно 378. Второй узел содержитобъектсозначением2837.Текущеезначение—4499.ИсходноуказательpCurrentсвязываетсясголовным узлом.
Поэтому при обращении к переменной next объекта, на который указываетpCurrent, возвращается адрес второго узла. Следовательно, условное выражение в строке 246возвратитFalse.Указатель pCurrent устанавливается на следующий узел, и цикл повторяется. Теперьпроверкавстроке246приводиткположительномурезультату.Еслиследующегоэлементанет,тоновыйузелвставляетсявконецсписка.На четвертом цикле вводится номер детали 3000. Дальнейшее выполнение программынапоминаетпредыдущийэтап,однаковэтомслучаетекущийузелимеетномер2837,азначениеследующегоузларавно4499.Проверкавстроке256возвращаетTRUE,иновыйузелвставляетсямеждудвумясуществующими.Когда пользователь вводит 0, условное выражение в строке 279 возвращает TRUE и циклwhile(1) прерывается.
В строке 300 функция-член Display() присваивается указателю нафункции-члены pFunc. В профессиональной программе присвоение должно проходитьдинамически,основываясьнавыборепользователем.Указатель функции-члена передается методу Iterate класса PartsList. В строке 208 методIterate()проверяет,неявляетсялисписокпустым.Затемвстроках213—215последовательноспомощьюуказателяфункции-членавызываютсяизспискавсеобъектыPart.ВитогедляобъектаPart вызывается соответствующий вариант метода Display(), в результате чего для разныхобъектоввыводитсяразнаяинформация.Неделя№3ОсновныевопросыИтак,двенеделиизученияC++ужепозади.Сейчасвынавернякасвободноориентируетесьв некоторых достаточно сложных аспектах объектно-ориентированного программирования,включаяинкапсуляциюиполиморфизм.ЧтодальшеПоследняя неделя начинается с изучения дополнительных возможностей наследования.Затемназанятии16выизучитепотоки,аназанятии17познакомитесьсоднимзамечательнымдополнением стандартов C++ — пространствами имен.
Занятие 18 посвящено анализу основобъектно-ориентированногопрограммирования.Вэтотденьвниманиебудетсконцентрированоне столько на синтаксисе языка, сколько на изучении концепций объектно-ориентированногопрограммирования.Назанятии19выпознакомитесьсиспользованиемшаблонов,аназанятии20узнаетеометодахотслеживанияисключительныхситуацийиошибок.Наконец,напоследнемзанятиибудутраскрытынекоторыехитростиисекретыпрограммированиянаC++,чтосделаетваснастоящимгурувэтойобласти.День15-й.ДополнительныевозможностинаследованияДо настоящего момента вы использовали одиночное и множественное наследование длясозданияотносительнопростыхсвязеймеждуклассами.Сегоднявыузнаете:•Чтотакоевложениеикакегоиспользовать•Чтотакоеделегированиеикакегоиспользовать•Каквыполнитьодинклассвнутридругого•КакиспользоватьзакрытоенаследованиеВложениеАнализируяпримеры,приведенныенапредыдущихзанятиях,вы,вероятно,заметили,чтовклассах допускается использование в переменных-членах объектов других классов.