М
И Н И СТ Е РСТ В О О Б РА ЗО В А Н И Я И Н А У К И
Ф
ИВ АНО КАФ
РО
ССИ Й СК О Й
Ф
Е Д Е РА Ц И И
Е Д Е РА Л Ь ...
94 downloads
71 Views
709KB Size
Report
This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Report copyright / DMCA form
М
И Н И СТ Е РСТ В О О Б РА ЗО В А Н И Я И Н А У К И
Ф
ИВ АНО КАФ
РО
ССИ Й СК О Й
Ф
Е Д Е РА Ц И И
Е Д Е РА Л Ь Н О Е А Г Е Н Т СТ В О ПО О Б РА ЗО В А Н И Ю В СК И Й Г О СУ Д А РСТ В Е Н Н Ы Й У Н И В Е РСИ Т Е Т
Е Д РА В Ы ЧИ СЛ И Т Е Л Ь Н О Й И ПРИ К Л А Д Н О Й М А Т Е М А Т И К И
В В Е Д Е Н И Е В О Б ЪЕ К Т Н О -О РИ Е Н Т И РО В А Н Н О Е ПРО Г РА М М И РО В А Н И Е Н А Я ЗЫ К Е OBJECT PASCAL
М етодические указания к курсукомп ь ю терны х наук
И в анов о И з д ат ел ь с т в о«И в анов с кий гос уд арс т в енны й унив ерс ит ет » 2005
С оста вител ь: с т арш ий препод ав ат ел ь кафед ры в ы чис л ит ел ь ной и прикл ад ной м ат ем ат ики Е . В . Со ко лов
Рас с м ат рив ают с я ос нов ны е понят ия объект но-ориент иров анного програм м иров ания и егореал из ация в яз ы ке Object Pascal, яв л яющем с я час т ь ю с ред ы раз работ ки Borland Delphi. Пред наз начает с я с т уд ент ам 2-3 курс ов м ат ем ат ичес кого факул ьт ет а, обучающим с я по с пециал ь нос т и/направ л ению «М ат ем ат ика» и направ л ению «М ат ем ат ика. Ком пь ют ерны е науки» .
П е ча та е тся по ре ш е н ию методиче ской комиссии ма тема тиче ског о фа кул ьте та Ива н овског ог осуда рстве н н ог о ун иве рсите та
Ре це н зе н т канд ид ат физ ико-м ат ем ат ичес кихнаук, д оцент Н . И . Я ц кин
Ó И з д ат ел ь с т в о«И в анов с кий гос уд арс т в енны й унив ерс ит ет » , 2005
СО Д Е РЖ А Н И Е В ведение ....................................................................................................... 4 1. М одуль но е п ро граммирование ............................................................ 6 1.1. М од ул и и ихс т рукт ура ................................................................... 6 1.2. И с пол ь з ов ание м од ул ей ................................................................ 11 1.3. Ос обеннос т и под кл ючения м од ул ей ............................................ 13 Упражнения ........................................................................................... 15 2. О б ъектно е п ро граммирование .......................................................... 16 2.1. Кл ас с ы и объект ы .......................................................................... 16 2.2. И с пол ь з ов ание кл ас с ов ................................................................. 18 2.3. Реал из ация кл ас с а ......................................................................... 22 2.4. Обл ас т и в ид им ос т и чл енов кл ас с а .............................................. 23 2.5. Конс т рукторы и д ес т рукторы ....................................................... 30 Упражнения ........................................................................................... 33 3. О б ъектно -о риентированно е п ро граммирование ........................... 34 3.1. Н ас л ед ов ание ................................................................................. 34 3.2. Сов м ес т им ос т ь кл ас с ов и ее ос обеннос т и .................................. 40 3.3. Опред ел ение т ипа объект а в ов рем я в ы пол нения ...................... 42 3.4. Ст ат ичес кое и д инам ичес кое с в яз ы в ание ................................... 45 3.5. Абс т ракт ны е м етод ы и кл ас с ы ..................................................... 51 Упражнения ........................................................................................... 55 4. Реализац ияо б ъектно й модели ........................................................... 56 4.1. Указ ат ел и на под програм м ы ......................................................... 56 4.2. Абс т ракт ны е кл ас с ы и обобщенны е ал горит м ы ......................... 59 4.3. Н ас л ед ов ание ................................................................................. 62 4.4. Динам ичес кое с в яз ы в ание ............................................................ 66 4.5. Вопрос ы опт им из ации .................................................................. 69 Заклю чение ............................................................................................... 77 Ч точит ат ь д ал ь ш е? .............................................................................. 79
3
В В Е Д Е НИЕ Объект но-ориент иров анное програм м иров ание пол учил орас прос т ранение в о в торой пол ов ине 80-хгод ов прош л ого в ека прежд е в с его как э ффект ив ная т ехнол огия раз работ ки бол ь ш ихпроектов . Как с л ед с т в ие, его ид еол огию и преим ущес т в а л егче понят ь чел ов еку, уже им еющем у опы т под обной работ ы с ис пол ь з ов анием т рад иционны хс ред с т в . В тоже в рем я в ряд л и цел ес ообраз ноприобрет ат ь т акой опы т л иш ь рад и из учения нов огопод ход а. В нас тоящем пос обии пред принят а попы т ка показ ат ь , как пол учит ь в ы игры ш от ис пол ь з ов ания объект ной т ехнол огии д аже в от нос ит ел ь но небол ь ш ихпрограм м ах. И з л ожение с т ав ит с в оей цел ь ю в ы пол нит ь наибол ее пл ав ны й переход от процед урногок объект но-ориент иров анном у програм м иров анию, не т ребуя от чит ат ел я с раз у же начат ь «м ы с л ит ь в т ерм инахООП» . Пред пол агает с я, чтоз наком с т в ос нов ы м и в оз м ожнос тям и и прим ерам и их прим енения пом ожет програм м ис т у оценив ат ь э ффект от ис пол ь з ов ания объект ногопод ход а в егопроект е и раз рабат ы в ат ь с т рукт уру кл ас с ов , ис ход я из ожид аем ы хрез ул ь т атов . Пос кол ь ку из д ание ориент иров анов перв ую очеред ь на с л уш ат ел ей кл ас с ичес кихунив ерс ит етов , в нем уд ел яет с я опред ел енное в ним ание с в яз ям програм м иров ания и т еорет ичес кой м ат ем ат ики. Раз в ет в л енная с ис т ем а абс т ракт ны хпонят ий пос л ед ней поз в ол яет л егкопрод ем онс т риров ат ь как преим ущес т в а нов ы хм еханиз м ов , т ак и ограниченнос т ь ихв оз м ожнос т ей. И з л ожение в д анном пос обии в ед ет с я с ис пол ь з ов анием яз ы ка Object Pascal, которы й яв л яет с я час т ь ю с ред ы раз работ ки Borland Delphi (в с е прив од им ы е прим еры т ес т иров ал ис ь в с ед ь м ой в ерс ии пакет а). Х орош оиз в ес т но, чтоПас кал ь раз рабат ы в ал с я Н . Виртом прежд е в с егокак учебны й яз ы к, и Object Pascal в опред ел енной м ере унас л ед ов ал э т у ос обеннос т ь . От с ут с т в ие в нем м ногихв оз м ожнос т ей, прис ущих C++ и Java, на начал ь ном э т апе из учения оказ ы в ает с я с корее преим ущес т в ом , чем нед ос т ат ком . Вм ес т е с т ем , объект ны е м од ел и э т ихт рехяз ы ков им еют м ногообщего, т ак чтос фера прим енения пол ученны хз наний не ограничив ает с я л иш ь програм м иров анием в Delphi. Перв ая час т ь пос обия с од ержит необход им ы е с в ед ения о с оз д ании библ иот ек. Х отя раз биение ис ход ного код а проект а на отд ел ь ны е м од ул и не им еет прям огоот нош ения к объект ном у програм м иров анию, без него не м огут бы т ь в пол ной м ере реал из ов аны абс т ракт ны е т ипы д анны х, опис анию раз работ ки которы хпос в ящена в торая час т ь . Т рет ь я 4
час т ь показ ы в ает, каким образ ом м еханиз м ы объект но-ориент иров анногопрограм м иров ания ис пол ь з уют с я д л я с оз д ания обобщенны хал горит м ов , прим еним ы хк д анны м пот енциал ь но бес конечногом ножес т в а т ипов . Н аконец, в чет в ертой час т и рас с м ат рив ают с я д в е нес кол ь кораз л ичны е реал из ации объект ной м од ел и и произ в од ит с я с рав нение в оз м ожнос т ей нов ой т ехнол огии и процед урногопрограм м иров ания. Сл ед ует от м ет ит ь , что круг в опрос ов , с в яз анны х с объект ны м под ход ом , не ис черпы в ает с я перечис л енны м и т ем ам и. Пол нос т ь ю з а рам кам и обс ужд ения ос т ал ис ь , в час т нос т и, кл ас с ы -конт ейнеры и м ножес т в енное нас л ед ов ание. Поэ той причине в пос обии не упом инают с я кл ас с ов ы е м етод ы и с с ы л ки, инт ерфейс ы объектов . Пред пол агает с я, чточит ат ел ь буд ет з наком ит ь с я с м атериал ом пос л ед ов ател ь но; текс т, которы й при перв ом прочт ении м ожно опус т ит ь , в ы д ел яет с я м ел ким ш рифтом . Дл я поним ания прим еров т ребует с я им еть пред с т ав л ение об ос нов ны хконс т рукцияхи т ипахяз ы ка Пас кал ь , в кл ючая з апис и, а т акже ос пос обахд инам ичес когоуправ л ения пам ят ь ю.
5
1. М О Д У Л ЬН О Е ПРО Г РА М М И РО В А Н И Е 1.1. М одули и их структура Соз д ание м од ул ей яв л яет с я с в яз ующим з в еном м ежд у процед урно-ориент иров анны м програм м иров анием , которое бы л онаибол ее попул ярной парад игм ой раз работ ки програм м ного обес печения в 1970-хгод ах, и информ ационно-ориент иров анны м (объект ны м ) програм м иров анием , с м енив ш им егов 1980-х. М одул ем в Object Pascal наз ы в ает с я с пециал ь ны м образ ом оформ л енная библ иот ека под програм м (процед ур и функций), которая м ожет т акже с од ержат ь объяв л ения нов ы хт ипов д анны х, перем енны хи конс т ант. Как и програм м у в цел ом , м од ул ь м ожнос ком пил иров ат ь . Рез ул ь т атом ком пил яции яв л яет с я файл , с очет ающий м аш инны й (пл ат форм оз ав ис им ы й) код под програм м м од ул я с т екс тов ы м и д анны м и: объяв л ениям и т ехже под програм м , а т акже т ипов , перем енны хи конс т ант, в в од им ы х в м од ул е. Сам ос тоят ел ь но з апус т ит ь на в ы пол нение э тот файл нел ь з я, од накоинформ ация из негов пос л ед с т в ии м ожет бы т ь ис пол ь з ов ана ком понов щиком (linker) д л я с борки програм м , в которы хим еют с я с с ы л ки на чл ены м од ул я. При э том упом янут ы е в ы ш е т екс тов ы е д анны е в ы с т упают в качес т в е опис ания с од ержим ого файл а, т ак что ис ход ны й код библ иот еки на э том э т апе оказ ы в ает с я уже не нужны м . Ос нов ное наз начение м од ул ей с ос тоит в раз биении бол ь ш ихпрограм м на от нос ит ел ь но нез ав ис им ы е час т и, с од ержащие л огичес ки с в яз анны е м ежд у с обой код и д анны е. Под обное раз биение упрощает с т руктуру програм м ы и поз в ол яет раз рабаты в ать отд ел ь ны е ее час т и нез ав ис им ои парал л ел ь но(в том чис л е раз л ичны м и л юд ь м и). Пом им оэ тогом од ул и неред ко в ы с тупают и в качес т в е конечного прод укт а, ес л и цел ь ю проект а яв л яет с я с оз д ание библ иот еки, которая м ожет м ногократнои без каких-л ибо из м енений ис пол ь з ов ат ь с я в раз ны хпрограм м ах. Как бы л о отм ечено в ы ш е, д л я под кл ючения т акой библ иот еки д ос т аточно л иш ь с ком пил иров анны хфайл ов , поэ том у появ л яет с я в оз м ожнос т ь раз работ ки ком м ерчес кихпрод уктов , не пред ос т ав л яющихд ос тупа к ис ход ном у код у. В Object Pascal ис ход ны й т екс т м од ул я хранит с я в од ном файл е с рас ш ирением pas. Э тот файл начинает с я с з арез ерв иров анного с л ов а unit (м од ул ь ), з а которы м через пробел с л ед ует им я м од ул я, з ав ерш аем ое точкой с з апятой. И м я м од ул я д ол жно с ов пад ат ь с им енем файл а, с од ержащегоегоис ход ны й код (в кл ючая регис т р с им в ол ов ). Заканчив а6
ет с я файл з арез ерв иров анны м с л ов ом end с точкой на конце. Прос т ранс т в о м ежд у з агол ов ком м од ул я и з ав ерш ающим end. раз бив ает с я на чет ы ре ра здел а : ин те рфе йса , ре а л иза ции, ин ициа л иза ции и фин а л иза ции (с л ед ующихим еннов т аком поряд ке). Раз д ел реал из ации начинает с я с кл ючев огос л ов а implementation и с инт акс ичес ки почт и ид ент ичен програм м е с той л иш ь раз ницей, что не с од ержит гл ав ногобл ока begin ¼ end. Б ол ее точноэ тооз начает, что з д ес ь м ожет рас пол агат ь с я л юбое кол ичес т в о опред ел ений процед ур и функций, с екций объяв л ения т ипов , перем енны хи конс т ант, с л ед ующих в произ в ол ь ном поряд ке. При э том , од нако, не с л ед ует з абы в ат ь , что ид ент ификаторы , как прав ил о, д ол жны бы т ь объяв л ены д ос в оегоперв огоис пол ь з ов ания. Раз д ел объяв л ений ил и инт ерфейс а (interface) м ожет с од ержат ь объяв л ения т ипов , перем енны хи конс т ант, им ена которы хд ол жны бы т ь уникал ь ны в пред ел ахм од ул я. Кром е того, с юд а пом ещают объяв л ения некоторы х(необяз ат ел ь но в с ех) под програм м , опред ел енны хв раз д ел е реал из ации 1. Е с л и объяв л ение под програм м ы наход ит с я в раз д ел е инт ерфейс а, тов раз д ел е реал из ации с пис ок ее парам ет ров м ожноопус т ить . В прот ив ном с л учае он д ол жен в точнос ти с оот в ет с тв ов ать прив ед енном у ранее. Вс е чл е н ы м од ул я (т ипы д анны х, перем енны е, конс т ант ы , процед уры и функции), перечис л енны е в раз д ел е инт ерфейс а, наз ы в ают с я открытыми ( public); чл ены , объяв л енны е л иш ь в раз д ел е реал из ации — за крытыми ( private). Объяс нение э т им т ерм инам буд ет д анов с л ед ующем параграфе. Раз д ел ы инициал из ации и финал из ации (начинающиес я с л ов ам и initialization и finalization, с оот в ет с т в енно) яв л яют с я необяз ат ел ь ны м и. Кром е того, раз д ел финал из ации м ожет ис пол ь з ов ат ь с я л иш ь в м од ул ях, с од ержащихбл ок инициал из ации. Синт акс ичес ки оба э т ихраз д ел а э кв ив ал ент ны т ел у процед уры ил и функции. В час т нос т и, они не д ол жны с од ержат ь никакихобъяв л ений. 1
Зд ес ь и д ал ее под объявл е н ием под програм м ы поним ает с я в ы ражение, которое с ос тоит из кл ючев огос л ов а procedure ил и function, з а которы м с л ед уют им я под програм м ы , с пис ок ее парам ет ров и т ип в оз в ращаем огоз начения (в с л учае функции). Пол ное опис ание под програм м ы , в кл ючающее ее з агол ов ок (header), т ел о(body) и перечень л окал ь ны хконс т ант, перем енны хи под програм м , наз ы в ает с я определ е н ием. Э т а т ерм инол огия отл ичает с я от принятой в оригинал ь ной д окум ент ации, гд е опред ел ение (в наш ем с м ы с л е) наз ы в ает с я объяв л ением (declaration).
7
Ос нов ное наз начение раз д ел а инициал из ации — з ад ание с т артов ы х з начений перем енны х (наприм ер, с пом ощь ю д ат чика с л учайны х чис ел ) и раз м ещение в пам ят и д инам ичес кихс т рукт ур д анны х. Раз д ел финал из ации ис пол ь з ует с я д л я ос в обожд ения рес урс ов , которы е м огл и бы т ь з анят ы в процес с е в ы пол нения раз д ел а инициал из ации и под програм м из раз д ел а реал из ации. Т ипичны м и з ад ачам и яв л яют с я уд ал ение д инам ичес ки с оз д анны хперем енны х, з акры т ие файл ов и т. п. Сл ед ует учит ы в ат ь , что раз д ел финал из ации м ожет бы т ь в ы з в ан д оз ав ерш ения раз д ел а инициал из ации. Т акая с ит уация в оз никает, ес л и в процес с е в ы пол нения пос л ед негопояв л яет с я ош ибка ил и, гов оря бол ее точно, необработ анная ис кл ючит ел ь ная с ит уация. В з акл ючение э тогопараграфа прив ед ем прим ер реал из ации библ иот еки д л я работ ы с рационал ь ны м и чис л ам и. Она в кл ючает опред ел ения нов огот ипа Rational — з апис и с д в ум я пол ям и цел огот ипа num и den д л я хранения чис л ит ел я и з нам енат ел я — и в ос ь м и под програм м , работ ающихс д анны м и э тогот ипа. Перв ы е чет ы ре: функции Plus, Minus, Dot и Slash, пред с т ав л яют с обой обы чны е операторы с л ожения, в ы чит ания, ум ножения и д ел ения рационал ь ны хчис ел . Ос т ав ш иес я процед уры реал из уют т ак наз ы в аем ы е опе ра торы с присвое н ием. И хотл ичие с ос тоит в том , чторез ул ьт ат в ы пол нения с оотв ет с т в ующей операции с охраняет с я в перв ом аргум ент е. Т ак, наприм ер, в ы з ов процед уры Add: Add(r1, r2);
э кв ив ал ент ен в ы ражению r1 := Plus(r1, r2);
Очев ид но, что ис пол ь з ов ание перв ой инс т рукции э ффект ив нее: ис чез ает л иш няя операция прис в аив ания. Кром е того, в с л ед ующей час т и м ы ув ид им , чтораз рабат ы в ат ь и ис пол ь з ов ать операторы с прис в оением иногд а оказ ы в ает с я проще, чем обы чны е ал гебраичес кие операции. Пример 1.1. Б ибл иот ека д л я работ ы с рационал ь ны м и чис л ам и. unit UnitRational; interface // объявление типа Rational type Rational = record // рациональное число num: Integer; // числитель (numerator) den: Integer; // знаменатель (denominator) end;
8
// объявления подпрограмм function Plus(r1,r2:Rational): Rational; function Minus(r1,r2:Rational): Rational; function Dot(r1,r2:Rational): Rational; function Slash(r1,r2:Rational): Rational; procedure Add(var r1:Rational; r2:Rational); procedure Subtract(var r1:Rational; r2:Rational); procedure MultiplyBy(var r1:Rational; r2:Rational); procedure DivideBy(var r1:Rational; r2:Rational);
// // // // // // // //
Плюс Минус Точка Косая черта Прибавить Вычесть УмножитьНа РазделитьНа
{ Поскольку результат выполнения перечисленных выше процедур сохраняется в первом аргументе, этот аргумент должен быть передан по ссылке, а не по значению. } implementation uses SysUtils; // модуль включает поддержку исключений procedure Normalize(var r:Rational); { Процедура приводит рациональное число r, передаваемое в качестве параметра, к нормальной форме: числитель - целое число, знаменатель - натуральное. } begin if r.den<0 then begin r.num:=-r.num; r.den:=-r.den; end; end; procedure Reduce(var r:Rational); { Процедура сокращает числитель и знаменатель числа r на их наибольший общий делитель. } var a,b:Integer; begin // используется алгоритм Евклида поиска НОД a:=Abs(r.num); b:=r.den; while a<>0 do if a>=b then a:=a-b else b:=b-a; // по завершении цикла b=НОД(r.num,r.den) r.num:=r.num div b; r.den:=r.den div b; end; // определения подпрограмм, объявленыых в разделе интерфейса function Plus(r1,r2:Rational): Rational; var res: Rational; begin res.num:=r1.num*r2.den+r1.den*r2.num; res.den:=r1.den*r2.den; Normalize(res); Reduce(res);
9
result:=res; { Последняя инструкция эквивалентна выражению Plus:=res; Локальная переменная result неявно объявляется в каждой функции и имеет тип, совпадающий с типом возвращаемого значения. По завершении работы функции содержимое этой переменной передается вызывающей программе. } end; procedure Add(var r1:Rational; r2:Rational); var _num,_den:Integer; begin _num:=r1.num*r2.den+r1.den*r2.num; _den:=r1.den*r2.den; r1.num:=_num; r1.den:=_den; Normalize(r1); Reduce(r1); end; function Slash(r1,r2:Rational): Rational; var res: Rational; begin if r2.num=0 then raise Exception.Create('Division by zero'); { Если числитель второго аргумента равен нулю, возбуждается исключение с сообщением "Деление на ноль". } res.num:=r1.num*r2.den; res.den:=r1.den*r2.num; Normalize(res); Reduce(res); result:=res; end; procedure DivideBy(var r1:Rational; r2:Rational); var _num,_den:Integer; begin if r2.num=0 then raise Exception.Create('Division by zero'); _num:=r1.num*r2.den; _den:=r1.den*r2.num; r1.num:=_num; r1.den:=_den; Normalize(r1); Reduce(r1); end; ... { Определение оставшихся четырех процедур и функций совершенно аналогично и потому здесь опускается. В реальном коде оно обязательно должно присутствовать. } end.
10
Отл ичит ел ь ной ос обеннос т ь ю функции Slash и процед уры DivideBy яв л яет с я час т ичны й характ ер в ы пол няем ы хим и операций. Е с л и в торой ихаргум ент оказ ы в ает с я рав ны м нул ю, в оз никает искл ю чител ьн а я ситуа ция — с ос тояние програм м ы , при котором д ал ь нейш ее в ы пол нение т екущей под програм м ы нев оз м ожно. Воз бужд ение ис кл ючения, ос ущес т в л яем ое оператором raise, поз в ол яет прерв ат ь в ы чис л ения и пред ос т ав ит ь в ы з ы в ающей програм м е прав ос ам ой принят ь реш ение, как преод ол ет ь появ ив ш уюс я неод ноз начнос т ь . Она м ожет л ибо обра бота ть искл ю чител ьн ую ситуа цию , ес л и т а не яв л яет с я крит ичной д л я ос нов ного ал горит м а, л ибов с в ою очеред ь прекрат ит ь в ы пол нение с в ы д ачей с оот в ет с т в ующего с ообщения пол ь з ов ател ю. По ум ол чанию ис пол ь з ует с я в торой в ариант. М еханиз м ис кл ючений яв л яет с я э ффект ив ной ал ьт ернат ив ой за щищ е н н ой те хн ике прог ра ммирова н ия, ос нов анной на анал из е в оз в ращаем ы хт ем ил и ины м с пос обом код ахош ибки. 1.2. И сп о ль зование модулей Соз д анны й м од ул ь м ожет бы т ь ис пол ь з ов ан как в ос нов ной програм м е, т ак и в д ругом м од ул е; ихназ ы в ают кл ие н та ми д анногом од ул я. Под кл ючение ос ущес т в л яет с я пос ред с т в ом оператора uses (ис пол ь з ует ): uses ИмяМодуля;
В програм м е в с е операторы uses д ол жны с тоят ь с раз у пос л е з агол ов ка, в м од ул е — пос л е с л ов interface ил и implementation. В од ном операторе uses м ожноперечис л ит ь и нес кол ь коим ен, раз д ел ив ихз апят ы м и. Пос л е под кл ючения м од ул я кл иент у с т анов ят с я д ос т упны л иш ь его от кры т ы е чл ены (т. е. т е, которы е бы л и объяв л ены в с екции инт ерфейс а). Обращение к ним произ в од ит с я т ак, как ес л и бы они бы л и объяв л ены в с ам ом кл иент е. В час т нос т и, перем енны е и конс т ант ы с т анов ят с я гл обал ь ны м и. Воз м ожна, од нако, с ит уация, когд а раз ны е м од ул и с од ержат от кры т ы е чл ены с од инаков ы м и им енам и. В э том с л учае обращение к чл ену конкрет ногом од ул я ос ущес т в л яет с я при пом ощи в ы ражения ИмяМодуля.ИмяЧлена
Е с л и ис пол ь з ует с я т акая з апис ь , тогов орят, чтоим я чл ена ква л ифицирова н о им енем м од ул я. Б ол ее под робно пробл ем а перекры в ающихся объяв л ений рас с м ат рив ает с я в § 1.3.
11
В качес т в е прим ера рас с м от рим небол ь ш ую програм м у, т ес т ирующую библ иот еку из пред ы д ущегопараграфа. Пример 1.2. Т ес тов ая програм м а д л я библ иот еки из прим ера 1.1. program TestRational; {$APPTYPE CONSOLE} // директива компилятора, определяющая консольное // приложение uses UnitRational; // подключаем модуль нашей библиотеки var r1: Rational = (num: 1; den: 2); r2: Rational = (num: -2; den: 6); ra,rs,rm,rd: Rational; { Поскольку тип Rational объявлен в интерфейсной части модуля, мы можем использовать его для объявления переменных. После старта приложения переменные r1 и r2 инициализируются значениями 1/2 и -2/6. } begin ra:=Plus(r1,r2); // обращение к функциям и процедурам модуля rs:=Minus(r1,r2); // осуществляется просто по их именам, rm:=Dot(r1,r2); // однако допустимы и выражения вида rd:=Slash(r1,r2); // ra:=UnitRational.Plus(r1,r2); WriteLn('Plus: ',ra.num,'/',ra.den); WriteLn('Minus: ',rs.num,'/',rs.den); WriteLn('Dot: ',rm.num,'/',rm.den); WriteLn('Slash: ',rd.num,'/',rd.den); Add(ra,r2); Subtract(rs,r2); MultiplyBy(rm,r2); DivideBy(rd,r2); WriteLn('Add: ',ra.num,'/',ra.den); WriteLn('Sub: ',rs.num,'/',rs.den); WriteLn('Mul: ',rm.num,'/',rm.den); WriteLn('Div: ',rd.num,'/',rd.den); ReadLn; // ввод необходим только для предотвращения // автоматического закрытия окна консоли end.
В процес с е работ ы програм м а в ы в од ит с л ед ующую информ ацию: Plus: Minus: Dot: Slash:
1/6 5/6 -1/6 -3/2
12
Add: Sub: Mul: Div:
-1/6 7/6 1/18 9/2
Зд ес ь с т роки 3 и 7 д ем онс т рируют, в час т нос т и, коррект ную работ у процед уры Reduce, а 4 и 8 — т акже и процед уры Normalize. Обе они яв л яют с я з акры т ы м и, поэ том у непос ред с т в енное упот ребл ение в т ес тов ой програм м е в ы ражений т ипа Normalize(r2); Reduce(r2); прив ед ет к ош ибке на э т апе ком пил яции. В д ейс т в ит ел ь нос т и, раз д ел ение чл енов м од ул ей на от кры т ы е и з акры т ы е яв л яет с я од ним из ос нов ны хпреим ущес т в ис пол ь з ов ания пос л ед них. Онопоз в ол яет с кры т ь от кл иент а час т ь реал из ации ал горит м а, которую в пос л ед с т в ии м ожнобуд ет м од ифициров ат ь , не в нос я никаких из м енений в ис ход ны й код д анногокл иент а. Б ол ее того, ес л и библ иот ека яв л яет с я д инам ичес ки з агружаем ой, тоее кл иент ы не прид ет с я д аже переком пил иров ат ь . 1.3. О со б енно сти п одклю чениямодулей В э том параграфе нам буд ет уд обнос чит ат ь ос нов ную програм м у м од ул ем , в котором нет раз д ел а инт ерфейс а, а рол ь раз д ел ов инициал из ации и финал из ации играет гл ав ны й бл ок begin ¼ end. Операторы uses опред ел яют от нош ения з ав ис им ос т и м ежд у м од ул ям и, которы е очев ид ны м образ ом м огут бы т ь пред с т ав л ены в в ид е ориент иров анного г ра фа за висимосте й. Е с л и с ущес т вует пут ь , с в яз ы в ающий в э том графе в ерш ину, с оот в ет с т в ующую м од ул ю A, с в ерш иной, с оотв ет с т в ующей м од ул ю B, тогов орят, чтом од ул ь A косве н н о испол ьзуе т м од ул ь B. Пос кол ь ку граф з ав ис им ос т ей м ожет им ет ь практ ичес ки л юбой в ид , в оз м ожнос т ь кос в енного ис пол ь з ов ания порожд ает некоторы е пробл ем ы , которы е необход им оучит ы в ать при раз работ ке м од ул ей. Прежд е в с его з ам ет им , что м од ул ь м ожет кос в енно ис пол ь з ов ат ь с ам с ебя. Т акая с итуация с оот в ет с т в ует цикл у на графе и наз ы в ает с я цикл иче ской ссыл кой м од ул ей. Ком пил ятор з апрещает цикл ичес кую с с ы л ку, ес л и в с е с оотв ет с т в ующие ей операторы uses рас пол ожены в инт ерфейс ны хчас тяхм од ул ей. Т ем с ам ы м ис кл ючает с я в оз м ожнос т ь появ л ения с ам ос с ы л ающихсяобъяв л ений т ипов д анны х. От с юд а в ы т екает с л ед ующее прав ил о: под кл ючение м од ул я в раз д ел е инт ерфейс а в ы пол няет с я л иш ь тогд а, когд а его перем енны е, конс т ант ы ил и т ипы ис пол ь з уют с я в объяв л енияхд анногораз д ел а. Вов с ех 13
ос т ал ь ны хс л учаяхс оот в ет с т в ующий оператор uses пом ещает с я в раз д ел реал из ации. Поряд ок под кл ючения м од ул ей опред ел яет и поряд ок с рабат ы в ания ихраз д ел ов инициал из ации и финал из ации, причем с ущес т в енны м и з д ес ь оказ ы в ают с я не тол ь ков ид графа з ав ис им ос т ей, нои очеред нос т ь перечис л ения м од ул ей в операторах uses. Пос л ед ов ат ел ь нос т ь в ы пол нения э т ихраз д ел ов опред ел яет с я инд укт ив нос л ед ующим образ ом . Дл я м од ул я, не ис пол ь з ующегод ругихм од ул ей, процед урой инициал из ации буд ем наз ы в ат ь прос той в ы з ов его раз д ел а инициал из ации, ес л и тол ь коон уже не бы л в ы пол нен ранее. Пус т ь т еперь м од ул ь A под кл ючает м од ул и B1, B2, ¼, Bn (им еннов т аком поряд ке). Тогд а процед ура инициал из ации м од ул я A буд ет с ос тоят ь в пос л ед ов ат ел ь ном в ы з ов е процед ур инициал из ации м од ул ей B1, B2, ¼, Bn и з ав ерш ат ь с я в ы пол нением раз д ел а инициал из ации м од ул я A (с нов а при ус л ов ии, что э то не бы л ос д ел аноранее). Т аким образ ом , процед ура инициал из ации ос нов ной програм м ы прив од ит к од нократ ном у ис пол нению раз д ел ов инициал из ации в с ехм од ул ей, кос в енноею ис пол ь з уем ы х. Раз д ел ы финал из ации в ы з ы в ают с я в обрат ном поряд ке. Рас с м от рим небол ь ш ой прим ер. Пус т ь програм м а Program1 с од ержит оператор uses Unit1, Unit2, Unit3; и пус т ь м од ул и Unit1 и Unit2 т акже ис пол ь з уют м од ул ь Unit3. Т огд а граф з ав ис им ос т ей м од ул ей в ы гл яд ит с л ед ующим образ ом : Program1 Unit1
Unit2
Unit3
Пос л е с т арт а прил ожения начинает с я инициал из ация ос нов ной програм м ы и перв ой в ы з ы в ает с я процед ура инициал из ации м од ул я Unit1. Пос кол ь ку он ис пол ь з ует Unit3, то с начал а в ы пол няет с я раз д ел инициал из ации Unit3, а з ат ем — Unit1. Дал ее с л ед ует процед ура инициал из ации м од ул я Unit2. Т ак как Unit3 уже бы л инициал из иров ан, она с в од ит с я к в ы з ов у раз д ел а инициал из ации Unit2. Т рет ь ей пос чет у в ы пол няет с я процед ура инициал из ации Unit3, которая прос то ничего не д ел ает. Т еперь нас тупает черед ос нов ного бл ока програм м ы , по з ав ерш ении работ ы которого раз д ел ы финал из ации м од ул ей ис пол няют с я в обрат ном поряд ке: Unit2, Unit1, Unit3. Э тот прим ер показ ы в ает, в час т нос т и, что пос л ед ов ат ел ь нос т ь инициал из ации м од ул ей не в с егд а очев ид на раз работ чику програм м ы . 14
Поэ том у в д окум ент ации к некоторы м библ иот екам жес т коогов арив ают пос л ед ов ат ел ь нос т ь их под кл ючения. Реком енд ует с я т акже перв ы м и в операторахuses указ ы в ат ь с т анд арт ны е м од ул и Delphi. Раз работ чик с ис т ем ы библ иот ек, раз ум еет с я, т акже д ол жен учит ы в ат ь опис анную в ы ш е неод ноз начнос т ь . Пробл ем ы з д ес ь м огут бы т ь с в яз аны , в час т нос т и, с попы т кам и раз л ичны хм од ул ей з ахв ат ит ь од ни и т е же рес урс ы . Преод ол ет ь э т у неприят нос т ь м ожно, наприм ер, пос ред с т в ом ус т анов ки с оот в ет с т в ующихфл агов . Н аконец, еще од на с л ожнос т ь в ы з в ана т ем , чтов раз л ичны хм од ул яхм огут бы т ь объяв л ены чл ены с од ним и и т ем и же им енам и. Э т и чл ены с кры в ают д руг д руга и кл ючев ую рол ь з д ес ь с нов а играет поряд ок перечис л ения м од ул ей в операторе uses. Е с л и м од ул ь A пос л ед ов ат ел ь но под кл ючает м од ул и B1, B2, ¼, Bn, то объяв л ения м од ул я Bi + 1 с кры в ают объяв л ения м од ул я Bi, а объяв л ения м од ул я A — в с е объяв л ения м од ул ей Bk, 1 £ k £ n. Т ем не м енее, д ос туп к чл енам конкрет ного м од ул я в с егд а м ожнопол учит ь , кв ал ифициров ав им яэ тогочл ена им енем м од ул я. В качес т в е ил л юс т рации с нов а рас с м от рим прим ер, прив ед енны й в ы ш е. Пред пол ожим , чтом од ул ь Unit3 с од ержит от кры т ую перем енную x т ипа Integer, а м од ул ь Unit2 — перем енную с т ем же им енем , но уже т ипа Real. Т огд а в м од ул яхUnit1 и Unit3 перем енная x буд ет цел ой, а в м од ул е Unit2 — д ейс т в ит ел ь ной, т ак как в нем объяв л ение из м од ул я Unit3 с кры в ает с я с обс т в енны м . В ос нов ной програм м е перем енная x с нов а буд ет цел ой, пос кол ь ку пос л ед ним в операторе uses с тоит Unit3, д ос т уп же к в ещес т в енной перем енной м од ул я Unit2 ос ущес т в л яет с я с пом ощь ю в ы ражения Unit2.x. У п раж нения 1.1. Раз работ айт е библ иот еку д л я работ ы с цел очис л енны м и в екторам и д л ины n, гд е n — некоторая конс т ант а. У казание. Пом им о из в ес т ны хм ат ем ат ичес кихопераций д л я нов огот ипа раз ум ноопред ел ит ь под програм м ы , которы е реал из уют д ейс т в ия, с пецифичны е д л я програм м иров ания: прис в аив ание, с рав нение, с оз д ание копии.
1.2. Т а же з ад ача, но д л я м ат риц раз м ера n ´ n, эл ем ент ам и которы х яв л яют с я рационал ь ны е чис л а. При в ы пол нении арифм ет ичес ких операций в э том с л учае с л ед ует ис пол ь з ов ат ь под програм м ы из м од ул я UnitRational.
15
2. О Б ЪЕ К Т Н О Е ПРО Г РА М М И РО В А Н И Е 2.1. К лассы и о б ъекты Б ибл иот ека, пос т роенная в параграфе 1.1, им еет ряд нед ос т ат ков . Во-перв ы х, не в с якое з начение перем енной т ипа Rational, д опус т им ое с точки з рения ком пил ятора, яв л яет с я т аков ы м и с точки з рения л огики програм м ы . Т ак, наприм ер, ес л и оба пол я num и den некоторой з апис и r с од ержат нул и (а им еннот акие з начения они пол учают при с т ат ичес ком раз м ещении перем енной в пам ят и), то перед ача r в качес т в е перв огопарам ет ра л юбой из от кры т ы хпод програм м библ иот еки прив од ит к бес конечном у в ы пол нению цикл а в процед уре Reduce и, как с л ед с т в ие, к з ав ис анию програм м ы . Т аким образ ом , програм м ис т, ис пол ь з ующий библ иот еку, пос тояннод ол жен с л ед ит ь з а т ем , чтобы пол я в с ех ис пол ь з уем ы хим перем енны хим ел и д опус т им ы е з начения. Н а э том ожнов оз раз ит ь , прав д а, чтопров ерку з начений парам ет ров на коррект нос т ь с л ед ует прос то д обав ит ь к реал из ации операций. Од наков с л ожны хал горит м ахв ы з ов ы под програм м проис ход ят гораз д о чаще, чем яв ное прис в аив ание, поэ том у т акое реш ение прив ед ет к с нижению с корос т и ихв ы пол нения. Вторая пробл ем а м енее очев ид на, нот акже в ес ь м а с ущес т в енна. И нт ерфейс м од ул я UnitRational показ ы в ает, что к перем енны м объяв л енного в нем т ипа прим еним ы операции с л ожения, в ы чит ания, ум ножения и д ел ения. Вм ес т е с т ем нет никакогос пос оба форм ал ь ногоопред ел ения того, чтот а ил и иная операция, наприм ер, в з ят ие ос т ат ка от д ел ения, к ним н е приме н има . Пол ь з ов ат ел ь библ иот еки в ообще не обяз ан с т роит ь с в ои програм м ы л иш ь на пред ос т ав л енны хв его рас поряжение процед урахи функциях, в м ес то э того он м ожет факт ичес ки нарав не в раз работ чиком с оз д ав ат ь с обс т в енны е под програм м ы , оперирующие д анны м т ипом . В д ейс т в ит ел ь нос т и, от м еченны е неприят нос т и яв л яют с я час т ны м и с л учаям и бол ее общей пробл ем ы : от кры тос т и в нут ренней с т рукт уры т ипа Rational. В пред ы д ущей час т и м ы уже гов орил и о том , что с окры т ие реал из ации библ иот еки с оз д ает в оз м ожнос т ь ее из м енения без с оот в ет с т в ующей м од ификации код а кл иентов . Т очно т ак же с окры т ие реал из ации т ипа д анны хд ает програм м ис т у-раз работ чику пол ную с в обод у в в ы боре конкрет ного пред с т ав л ения э того т ипа и в оз м ожнос т ь конт рол я цел ос т нос т и егообъектов . 16
Н о ес л и с т рукт ура т ипа не в ид на пол ь з ов ат ел ю, то что же тогд а с л ужит егоопис анием ? В качес т в е т аков огот еперь в ы с т упают свойства объектов д анного т ипа, то ес т ь операции, которы е к ним прим еним ы . Т ип д анны хс ос кры т ы м в нут ренним пред с т ав л ением , опис анны й в т ерм инахд опус т им ы хопераций, наз ы в ают а бстра ктн ым. Зам ет им , чтоабс т ракт ны е т ипы им еют м ногообщегос понят иям и с ов рем енной м ат ем ат ики. Кажд ое в в ед ение в т еорию м ножес т в от м ечает, чтоприрод а ихэл ем ентов не им еет никакогоз начения: м ножес т в а и кл ас с ы опред ел яют с я прос то как с ущнос т и, над которы м и м ожно в ы пол нят ь опред ел енны е операции. Т оже с ам ое в ернои в от нош ении л юбого д ругого м ат ем ат ичес кого понят ия. Сл ед с т в ием т акого под ход а яв л яет с я пов ы ш енная рез ул ь т ат ив нос т ь : конечны й итог того ил и иного ис с л ед ов ания как прав ил ооказ ы в ает с я прим еним не к од ном у, а к цел ом у кл ас с у конкрет ны хобъектов . Абс т ракт ны е т ипы д анны х в ы пол няют анал огичную функцию, поз в ол яя с оз д ав ат ь ал горит м ы , не з ав ис ящие от реал из ации т ипа и потом у обл ад ающие з начит ел ь но бóл ь ш им в рем енем жиз ни. Б ол ее того, появ л яет с я в оз м ожнос т ь раз рабат ы в ат ь обобщ е н н ые а л г оритмы, которы е од нов рем еннои без каких-л ибоиз м енений м огут бы т ь прим енены к д анны м пот енциал ь нобес конечногом ножес т в а т ипов . В Object Pascal, как и в бол ь ш инс т в е д ругихс ов рем енны хяз ы ков програм м иров ания, абс т ракт ны е т ипы реал из уют с я пос ред с т в ом кл а ссов. Кл ас с ом з д ес ь наз ы в ает с я т ип д анны х, объед иняющий в с ебе с т рукт уру, анал огичную з апис и, и набор под програм м д л я работ ы с ней. Перем енны е кл ас с ов огот ипа наз ы в ают объе кта ми. Опред ел ение кл ас с а с ос тоит из д в ухчас т ей: ин те рфе йса и ре а л иза ции. И нт ерфейс раз м ещает с я в раз д ел е type и им еет с л ед ующий в ид : ИмяКласса = class СписокЧленов end;
гд е И м я Клас с а — л юбой д опус т им ы й ид ент ификатор, С пи с о кЧ лено в — с пис ок объяв л ений чл енов кл ас с а: пол е й и ме тодов (в оз м ожно, пус той). В д ейс т в ит ел ь нос т и, яз ы к Object Pascal ис пол ь з ует еще од ин т ип чл енов кл ас с а: свойства ( properties), которы й поз в ол яет с в яз ы в ат ь м ежд у с обой пол я и м етод ы . Св оим проис хожд ением он обяз ан, по-в ид им ом у, ос обеннос тям с ред ы раз работ ки, ис пол ь з уем ой Delphi. В отл ичие от с в ойс т в абс т ракт ны хт ипов , пол нос т ь ю опис ы в ающихв оз м ожнос т и экз ем пл яров , с в ойс т в а кл ас с а ис пол ь з уют с я в ос нов ном д л я пред с т ав л ения кол ичес т в енны ххаракт ерис т ик объект а. Н ичего принципиал ь но нов ого в объект ную м од ел ь с в ойс т в а не д обав л яют, поэтом у д ал ее м ы онихгов орит ь не буд ем .
17
Пол я кл ас с а, т ак же как и пол я з апис и, пред с т ав л яют с обой обы чны е перем енны е, с группиров анны е в од ну с т рукт уру. М етод ы — э то под програм м ы д л я работ ы с д анной с т рукт урой. Объяв л ения пол ей и м етод ов анал огичны объяв л ениям , с оот в ет с т в енно, перем енны хи под програм м . Рас с м от рим Пример 2.1. И нт ерфейс кл ас с а «Т реугол ь ник» . type Triangle = class a,b,c: Real; function Square: Real; function Perimeter: Real; end;
// // // //
класс треугольник поля - длины сторон метод для вычисления площади метод для вычисления периметра
Прив ед енное объяв л ение показ ы в ает, чтообъект ы кл ас с а Triangle характ ериз уют с я т рем я д ейс т в ит ел ь ны м и чис л ам и — д л инам и с торон, и что к кажд ом у объект у прим еним ы операции в ы чис л ения пл ощад и и перим ет ра. Отл ожим на в рем я в опрос о реал из ации м етод ов и пос м от рим , как ис пол ь з ов ат ь готов ы е кл ас с ы в програм м е. 2.2. И сп о ль зование классов Как и д ругие т ипы д анны х, кл ас с ы д ос т аточноред кофигурируют с обс т в еннов код е ал горит м ов . Обы чноихим ена упот ребл яют с я в объяв л енияхобъектов — перем енны хи парам ет ров под програм м . Ос обеннос т ь ю объект ной м од ел и яз ы ка Object Pascal яв л яет с я то, что в с е объект ны е перем енны е в нем пред с т ав л яют с обой указ ат ел и (ссыл ки н а объе кты, object reference), ис ход ны е з начения которы х не опред ел ены . Дл я реал ь ногораз м ещения объект а в пам ят и ис пол ь з ует с я с л ед ующая инс т рукция: ИмяОбъекта := ИмяКласса.Create;
Она в ы д ел яет в пам ят и обл ас т ь , с од ержащую по од ном у э кз ем пл яру кажд огопол я кл ас с а, и прис в аив ает объект ной перем енной ее ад рес . Т аким образ ом , в опреки перв ом у в печатл ению, объект ы им еют почт и т акое же в нут реннее с т роение, как и перем енны е т ипа «з апис ь » . М етод ы по-прежнем у раз м ещают с я в пам ят и тол ь коод ин раз , в обл ас т и, от в ед енной д л я код а, и ихобъед инение с пол ям и в объяв л ении кл ас с а
18
нос ит чис тос инт акс ичес кий характ ер2. Н екоторое с ход с т в ом ежд у прив ед енны м в ы ш е в ы ражением и инс т рукцией в оз бужд ения ис кл ючения raise Exception.Create(...) не с л учайно. В д ейс т в ит ел ь нос т и, в с е ис кл ючения в Object Pascal яв л яют с я объект ам и кл ас с а Exception и, в ы з ы в ая их, м ы перед аем оператору raise ад рес в нов ь с оз д анногоэкз ем пл яра д анногокл ас с а, с од ержащегоопис ание ис кл ючит ел ь ной с ит уации. Впос л ед с т в ии эт а информ ация м ожет бы т ь ис пол ь з ов ана при обработ ке ис кл ючения, од нако под робное з наком с т в о с д анны м процес с ом не в ход ит в наш и пл аны .
Пос л е в ы д ел ения пам ят и обращение к пол ям объект а произ в од ит с я пос ред с т в ом оператора «точка» : ИмяОбъекта.ИмяПоля
Операция раз ы м енов ы в ания (^), необход им ая при работ е с д инам ичес ким и перем енны м и д ругихт ипов , д л я объект ны хс с ы л ок не т ребует с я. Опис анию с инт акс ис а в ы з ов а м етод ов пред пош л ем небол ь ш ое от с т упл ение. Как из в ес т но, в м ат ем ат ике прим еняют с я д в а с пос оба з апис и отображений: функционал ь ны й x(a, b) и операторны й a x b. Перв ы й яв л яет с я бол ее унив ерс ал ь ны м и ис пол ь з ует с я в т еории функций. Второй в с очет ании с о с в ойс т в ом ас с оциат ив нос т и поз в ол яет из бежат ь л иш них с кобок при з апис и ком поз иции отображений и характ ерен д л я ал гебры . Абс ол ют но никакой с од ержат ел ь ной раз ницы м ежд у э т им и с пос обам и нет и в ы бор в пол ь з у того ил и иного опред ел яет с я, гл ав ны м образ ом , с л ожив ш им ис я т рад ициям и. Яз ы ки програм м иров ания обы чно под д ержив ают обе форм ы з апис и, рас ш иряя как понят ие оператора, т ак и понят ие функции. С появ л ением объектов к э т им форм ам д обав л яет с я еще од на, нес кол ь ков арь ирующаяс я от од ногояз ы ка к д ругом у. В Object Pascal д л я в ы з ов а м етод ов ис пол ь з ует с я инс т рукция в ид а ИмяОбъекта.ИмяМетода(Параметры);
которая с од ержат ел ь но(ноне с инт акс ичес ки!) э кв ив ал ент на в ы ражению ИмяМетода(ИмяОбъекта,Параметры); 2
Ч ит ател ь, в ероятно, уже з ам ет ил , чтотерм ин «объект » ис пол ь з ует с я нам и в д вух нес кол ь ко раз л ичны хс м ы с л ах: перем енная кл ас с ов ого типа и обл ас ть пам яти, ад рес уем ая этой перем енной. Второе бол ее точно, од накос точки з рения объектной ид еол огии конкрет ное пред с т ав л ение объект а не им еет з начения, поэтом у перв ая инт ерпрет ация т акже д опус т им а. Дал ее, гов оря о перем енной x как об объект е, м ы в с егд а буд ем под раз ум ев ать объект, ад рес которогос од ержит x.
19
Т аким образ ом , перв ы й аргум ент прос тос т ав ит с я перед им енем под програм м ы и отд ел яет с я от неготочкой. От с юд а с л ед ует, в час т нос т и, чтокажд ом у м етод у перед ает с я по крайней м ере од ин аргум ент — объект, д л я которогоэ тот м етод в ы з ы в ает с я. Данное прав ил о в пол не с оот в ет с т в ует поним анию м етод а как с в ойс т в а объект а: нел ь з я гов орит ь ос в ойс т в е, не указ ав , к чем у им енно оно от нос ит с я. Ст анов ит с я яс но т акже, почем у в рас с м от ренном в ы ш е прим ере м етод ы Square и Perimeter объяв л ены без парам ет ров : необход им ая информ ация буд ет перед ана им ав том ат ичес ки. Н ов ая, не с ов с ем обы чная, форм а з апис и в д ейс т в ит ел ь нос т и поз в ол яет нес кол ь кос ократ ит ь кол ичес т в оис пол ь з уем ы хс кобок, ув ел ичив ая т ем с ам ы м чит абел ь нос т ь в ы ражений. В с ам ом д ел е, ес л и эл ем ент д анны х, в оз в ращаем ы й м етод ом -функцией, в с в ою очеред ь яв л яет с я объектом , тод л я негос нов а м огут бы т ь в ы з в аны с оот в ет с т в ующие м етод ы . В рез ул ьт ат е, в оз никают пос л ед ов ат ел ь нос т и в ид а Объект.Метод1(¼).Метод2(¼).Метод3(¼).¼
гд е м етод с ном ером k от нос ит с я к кл ас с у, котором у принад л ежит объект, в оз в ращаем ы й м етод ом с ном ером k - 1.
Рас с м от рим , наконец, небол ь ш ую програм м у, д ем онс т рирующую работ у с объект ам и кл ас с а Triangle. Сам а в оз м ожнос т ь прив ес т и т акой прим ер уже с ейчас показ ы в ает, чтод л я ис пол ь з ов ания кл ас с а д ос т аточнол иш ь им ет ь пред с т ав л ение оегоинт ерфейс е и с ов ерш енноне обяз ат ел ь ноз нат ь , как им еннореал из уют с я м етод ы . Пример 2.2. И с пол ь з ов ание кл ас с а Triangle. program TestTriangle; {$APPTYPE CONSOLE} uses UnitTriangle;
// подключаем модуль, в котором // определен класс Triangle // объявление объектных переменных
var t1, t2: Triangle; begin t1:=Triangle.Create; // объекты размещаются в памяти и их адреса t2:=Triangle.Create; // присваиваются объектным ссылкам t1.a:=2; t2.a:=3; // полям объектов приписываются t1.b:=3; t2.b:=4; // конкретные числовые значения t1.c:=4; t2.c:=5; // WriteLn('Square of triangle 1: ', t1.Square); WriteLn('Perimeter of triangle 1: ', t1.Perimeter); WriteLn('Square of triangle 2: ', t2.Square); WriteLn('Perimeter of triangle 2: ', t2.Perimeter);
20
{ Для объектов t1 и t2 вызываются методы Square и Perimeter. Возвращаемые ими действительные значения выводятся на экран. } t1.Destroy; t2.Destroy; ReadLn; end.
// объекты удаляются из памяти
Прив ед енны й код с од ержит од ну нов ую операцию: уд ал ение объект а из пам ят и. Вообще, тот факт, что объект ная перем енная пред с т ав л яет с обой указ ат ел ь , с л ед ует в с е в рем я учит ы в ат ь при раз работ ке програм м ы . Впрочем , пробл ем ы , которы е з д ес ь в оз никают, характ ерны д л я л юбы хд инам ичес кихперем енны х. Прежд е в с его необход им о в ним ат ел ь но с л ед ит ь з а т ем , чтобы объект ной перем енной в с егд а бы л с опос т ав л ен ад рес с ущес т в ующего объект а. Попы т ка обращения к пол ю ил и м етод у объект а пос ред с т в ом перем енной, не с од ержащей реал ь ногоад рес а, не д иагнос т ирует с я ком пил ятором и, как прав ил о, в ы з ы в ает ош ибку (ис кл ючит ел ь ную с ит уацию кл ас с а Access violation — наруш ение д ос т упа) в ов рем я в ы пол нения програм м ы . Пос кол ь ку объект ы раз м ещают с я в пам ят и програм м ис том , он же д ол жен поз абот ит ь с я и об их с в оев рем енном уд ал ении. Дл я э того ис пол ь з ует с я инс т рукция ИмяОбъекта.Destroy;
Св ои ос обеннос т и им еют и операции над объект ам и. Т ак, прис в аив ание a:=b, гд е a и b — некоторы е объект ны е перем енны е, в л ечет з а с обой не копиров ание з начений пол ей, а с ов м ещение указ ат ел ей: пос л е нее обе перем енны е указ ы в ают на од ну и т у же обл ас т ь пам ят и. Е с л и д о э тогоперем енная a уже с од ержал а ад рес некоторой с т рукт уры , тот еперь д ос т уп к ней пол учит ь с корее в с егоне уд ас т с я. Соот в ет с т в ующую ос обеннос т ь им еет и с рав нение д в ухобъектов . Е с л и перем енны е a и b с од ержат раз л ичны е ад рес а, торез ул ьт атом операции a=b буд ет «л ожь » д аже в том с л учае, когд а э т и объект ы принад л ежат од ном у кл ас с у и ихпол я им еют од инаков ы е з начения. Н аконец, при ис пол ь з ов ании объект а в качес т в е парам ет ра под програм м ы , пос л ед ней перед ает с я его реал ь ны й ад рес . Т аким образ ом , появ л яет с я не в с егд а жел ат ел ь ная в оз м ожнос т ь м од ифициров ат ь д анны й объект в процес с е ее в ы пол нения. Объяв л ение парам ет ра как конс т ант ного(ис пол ь з ов ание префикс а const) не реш ает э т у пробл ем у.
21
2.3. Реализац иякласса Реал из ация кл ас с а пред с т ав л яет с обой набор опред ел ений в с ех егом етод ов . В Object Pascal в с е опред ел ения д ол жны наход ит ь с я з а пред ел ам и инт ерфейс а кл ас с а и не пред ш ес т в ов ат ь ем у. Б ол ее того, ес л и инт ерфейс , буд учи объяв л ением т ипа д анны х, м ожет рас пол агат ь с я как в раз д ел е инт ерфейс а м од ул я, т ак и в раз д ел е реал из ации, то реал из ация кл ас с а — тол ь ков раз д ел е реал из ации м од ул я. Синт акс ичес ки опред ел ения м етод ов отл ичают с я от опред ел ений обы чны хпод програм м л иш ь т ем , чтоихид ент ификаторам пред ш ес т в ует им я кл ас с а, от д ел яем ое т очкой (гов орят, чтоим я м ет од а ква л ифицируе тся им енем кл ас с а). Пос л ед ов ат ел ь нос т ь , в которой они прив од ят с я, не обяз ана в точнос т и пов торят ь пос л ед ов ат ел ь нос т ь объяв л ений в инт ерфейс е кл ас с а. Как уже бы л оот м еченов пред ы д ущем параграфе, кажд ом у м етод у, пом им о перечис л енны х в его объяв л ении парам ет ров , перед ает с я еще од ин — объект, д л я которого д анны й м етод в ы з в ан. Обрат ит ь с я к э том у объект у в нут ри реал из ации м етод а м ожно по им ени self (с ам ). Т аким образ ом , д л я работ ы с пол ям и и м етод ам и т екущегообъект а прим еняют с я в ы ражения в ид а self.ИмяПоля
и self.ИмяМетода(Параметры)
Пример 2.3. Реал из ация м етод ов кл ас с а Triangle. function Triangle.Perimeter: Real; begin result:=self.a+self.b+self.c; // возвращается сумма значений // полей текущего объекта end; function Triangle.Square: Real; var p:Real; begin p:=self.Perimeter/2; result:=Sqrt(p*(p-self.a)*(p-self.b)*(p-self.c)); { Площадь рассчитывается по формуле Герона. Для вычисления полупериметра используется предыдущий метод, вызываемый для объекта self. } end;
22
Рас с м от рим т еперь под робнее, как работ ает програм м а из прим ера 2.2. Ст рока WriteLn('Square of triangle 1: ', t1.Square);
прив од ит к в ы з ов у м етод а Square д л я объект а t1. В качес т в е парам ет ра ем у перед ает с я ад рес , которы й хранит перем енная t1. Сл ед ующей в ы пол няет с я перв ая с т рока из реал из ации м етод а Square: p:=self.Perimeter/2;
Зд ес ь тот же с ам ы й объект уже ад рес ует указ ател ь self. Т аким образ ом , м етод Perimeter с нов а в ы з ы в ает с я д л я объект а t1 и под с чит ы в ает с ум м у егопол ей. Дал ее ис пол нение в оз в ращает с я в м етод Square, з ав ерш ающий в ы чис л ение пл ощад и т реугол ь ника t1, и з ат ем в ос нов ную програм м у. Дв ум я с т рокам и ниже в ы з ов под програм м ы Square произ в од ит с я уже д л я объект а t2, поэ том у им енноегоад рес буд ут с од ержат ь указ ат ел и self в ход е в ы пол нения м етод ов Square и Perimeter и, с л ед ов ат ел ь но, им енноегопол я буд ут учас т в ов ат ь в в ы чис л ениях. Сл ед ует от м ет ит ь , что ис пол ь з ов ание указ ат ел я self не яв л яет с я обяз ат ел ь ны м : к пол ям и м етод ам т екущего объект а м ожно обращат ь с я прос то по ихим енам . М ы , од нако, не буд ем пока ис пол ь з ов ат ь эт у в оз м ожнос т ь .
2.4. О б ласти видимо сти членов класса Ос обеннос т и кл ас с ов , опис анны е д о с их пор, не д ают ничего принципиал ь но нов ого по с рав нению с м од ул ям и. Е д инс т в енны м з ам ет ны м отл ичием яв л яет с я бол ее чет кое опред ел ение перечня под програм м , которы е м ожноприм енит ь к з ад анном у т ипу. Э т а в оз м ожнос т ь , од нако, м ал ос пос обс т в ует д ос т ижению наш ей ос нов ной цел и — с оз д анию абс т ракт ны хт ипов , не поз в ол яющихпол ь з ов ат ел ю в ид ет ь д ет ал и с в оей реал из ации. Дл я реш ения д анной з ад ачи прим еняет с я с пециал ь ны й м еханиз м ограничения д ос т упа к чл енам кл ас с а — з ад ание ихобл а сте й видимости. В Object Pascal объяв л ению л юбого чл ена в инт ерфейс е кл ас с а м ожет пред ш ес т в ов ат ь од ноиз з арез ерв иров анны хс л ов : private (за крытый), protected (за щищ е н н ый), public (открытый), а т акже published (опубл икова н н ый) и automated (а втома тизирова н н ый), опред ел яющее егов ид им ос т ь . Е с л и не указ анони од ноиз них, точл ен им еет т у же обл ас т ь в ид им ос т и, чтои пред ш ес т в ующий ем у. Э топоз в ол яет объед инят ь 23
чл ены в группы , обл ад ающие од ним и т ем же уров нем д ос т упа, указ ы в ая егоод ин раз : ИмяКласса = class private { объявления закрытых членов } protected { объявления защищенных членов } public { объявления открытых членов } published { объявления опубликованных членов } automated { объявления автоматизированных членов } end;
Ч л ены , объяв л ения которы храс пол ожены в с ам ом начал е инт ерфейс а кл ас с а д оперв огояв ногоуказ ания обл ас т и в ид им ос т и, с чит ают с я опубл иков анны м и (ес л и програм м а ком пил ирует с я в с ос тоянии {$M+}) ил и от кры т ы м и (в с ос тоянии {$M-}). Закры т ы е чл ены кл ас с а в ид ны л иш ь в пред ел ах того м од ул я, в котором э тот кл ас с опред ел ен, от кры т ы е — в с юд у, гд е в ид ен с ам кл ас с . Защищенны е чл ены , пом им ом од ул я, с од ержащегоопред ел ение кл ас с а, д ос т упны т акже при реал из ации м етод ов т ехкл ас с ов , которы е яв л яют с я нас л ед никам и д анного (м еханиз м нас л ед ов ания буд ет под робно рас с м ат рив ат ь с я в § 3.1). Опубл иков анны е и ав т ом ат из иров анны е чл ены им еют т у же в ид им ос т ь , чтои от кры т ы е. И хотл ичия с в яз аны с в оз м ожнос тям и Delphi, которы е с ейчас не яв л яют с я д л я нас с ущес т в енны м и. Т аким образ ом , кл ас с ы пред ос т ав л яют т е же ос нов ны е уров ни д ос т упа, чтои м од ул и: от кры т ы й и з акры т ы й, од накос пос об ихз ад ания яв л яет с я з начит ел ь но бол ее гибким . При ис пол ь з ов ании т рад иционны х с л ожны х т ипов д анны х, в ид им ос т ь ихв нут ренней с т рукт уры цел иком опред ел яет с я в ид им ос т ь ю с ам ого т ипа. Кл ас с ы же поз в ол яют пол нос т ь ю с кры т ь от пол ь з ов ат ел я с в ое пред с т ав л ение, объяв ив в с е пол я з акры т ы м и, и при э том не ограничив ат ь его д ос т уп с обс т в енно к т ипу д анны х, пред ос т ав л яя т ем с ам ы м в оз м ожнос т ь с оз д ания с оот в ет с т в ующихперем енны х. Прив ед ем т еперь нов ую в ерс ию библ иот еки д л я работ ы с рационал ь ны м и чис л ам и, с в обод ную (почт и) от нед ос т ат ков , перечис л енны хв начал е § 2.1. Она буд ет с од ержат ь опред ел ение ед инс т в енного кл ас с а: Rational, и перв ое, что необход им о с д ел ат ь , — э то раз работ ат ь инт ерфейс д анногокл ас с а. 24
Пример 2.4. И нт ерфейс кл ас с а Rational. Rational = class // рациональное число private // закрытые члены класса num: Integer; den: Integer; protected // защищенные члены класса procedure Normalize; procedure Reduce; public // открытые члены класса function GetNum: Integer; function GetDen: Integer; procedure SetNum(value:Integer); procedure SetDen(value:Integer); function Plus(r:Rational): Rational; function Minus(r:Rational): Rational; function Dot(r:Rational): Rational; function Slash(r:Rational): Rational; procedure Add(r:Rational); procedure Subtract(r:Rational); procedure MultiplyBy(r:Rational); procedure DivideBy(r:Rational); end;
// числитель // знаменатель // Нормализовать // Сократить // // // // // // // // // // // //
Вернуть значение поля num Вернуть значение поля den Установить значение num Установить значение den Плюс Минус Точка Косая черта Прибавить Вычесть УмножитьНа РазделитьНа
И т ак, кл ас с Rational факт ичес ки объед инил в с ебе в с е чл ены м од ул я из § 1.1. При э том кол ичес т в опарам ет ров под програм м ум ень ш ил ос ь на ед иницу: перв ы й аргум ент т еперь перед ает с я им ав том ат ичес ки. Пол ученны е в рез ул ь т ат е объяв л ения бинарны хопераций в ы гл яд ят нес кол ь кос т ранно, од нако в програм м е ихв ы з ов буд ет им ет ь бол ее ес т ес т в енны й в ид . В с оотв ет с т в ии с общей ид еей с окры т ия реал из ации пол я кл ас с а объяв л ены з акры ты м и, а операции, которы е м ожно в ы пол нять над его объект ам и — от кры ты м и. Обл ас ть в ид им ос ти процед ур Normalize и Reduce нес кол ь коув ел ичена пос рав нению с пред ы д ущей в ерс ией. См ы с л д анногореш енияс т анет понятен тол ь копос л е из учения нас л ед ов анияв § 3.1. Ограничение д ос т упа к пол ям num и den прив ел о к том у, что пол ь з ов ат ел ь пот ерял в оз м ожнос т ь прис в аив ат ь объект ам кл ас с а Rational конкрет ны е з начения и уз нав ат ь рез ул ьт ат в ы пол нения операций. Дл я ком пенс ации э тогоэ ффект а в инт ерфейс кл ас с а д обав л ены м етод ы Get¼ и Set¼ Н ес м от ря на то, чтоуказ анны е м етод ы пред пол агает с я ис пол ь з ов ат ь д л я непос ред с т в енного обращения к пол ям , д анны й ш аг почт и не ум ень ш ает абс т ракт нос т и т ипа.
25
В с ам ом д ел е, л юбое рационал ь ное чис л оим еет цел ы е чис л ит ел ь и з нам енат ел ь , и необход им ос т ь хранит ь ихим еннов д в ухпол яхт ипа Integer никак не с л ед ует из объяв л ений нов ы хм етод ов . Од накояв ное указ ание с кал ярного (прос того) т ипа д л я парам ет ров и в оз в ращаем ы хз начений эт их под програм м ограничив ает д иапаз он д опус т им ы х з начений чис л ит ел я и з нам енат ел я. Б ол ее прав ил ь ны м бы л о бы опред ел ит ь с в ой кл ас с и д л я пред с т ав л ения цел ы хчис ел , нотогд а с оот в ет с т в ующие пробл ем ы в оз никл и бы уже д л я него. В д ейс т в ит ел ь нос т и, ед инс т в енны м реш ением , поз в ол яющим с оз д ав ат ь ис т инноабс т ракт ны е т ипы , яв л яет с я пол ны й от каз от ис пол ь з ов ания с кал ярны хт ипов д анны х. Яз ы ки, реал из ующие д анны й под ход , тоес т ь яв л яющиес я чис тообъект но-ориент иров анны м и, с ущес т в уют, од наков с е они проигры в ают яз ы кам с ос м еш анной м од ел ь ю в пл ане бы с т род ейс т в ия.
Зав ерш им т еперь раз работ ку библ иот еки, реал из ов ав м етод ы кл ас с а Rational. Пример 2.5. Опред ел ение м од ул я, с од ержащегокл ас с Rational. unit UnitRational; interface type Rational = class ... // здесь располагается интерфейс класса, приведенный выше end; implementation uses SysUtils; // модуль включает поддержку исключений function Rational.GetNum: Integer; begin result:=self.num; end; function Rational.GetDen: Integer; begin result:=self.den; end; procedure Rational.SetNum(value:Integer); begin self.num:=value; end; procedure Rational.SetDen(value:Integer); begin if value>0 then // знаменатель всегда должен быть положителен self.den:=value else // иначе возбуждается исключение с сообщением // "Недопустимое значение знаменателя"
26
raise Exception.Create('Illegal value of denominator'); end; procedure Rational.Normalize; begin if self.den<0 then begin self.num:=-self.num; self.den:=-self.den; end; end; procedure Rational.Reduce; var a,b:Integer; begin a:=Abs(self.num); b:=self.den; while a<>0 do if a>=b then a:=a-b else b:=b-a; self.num:=self.num div b; self.den:=self.den div b; end; procedure Rational.DivideBy(r:Rational); var _num,_den:Integer; begin if r.num=0 then raise Exception.Create('Division by zero'); _num:=self.num*r.den; _den:=self.den*r.num; self.num:=_num; self.den:=_den; self.Normalize; self.Reduce; end; function Rational.Slash(r:Rational): Rational; var res:Rational; begin if r.num=0 then raise Exception.Create('Division by zero'); res:=Rational.Create; // переменная res размещается в памяти res.num:=self.num*r.den; // теперь возможны res.den:=self.den*r.num; // обращения к ее полям res.Normalize; res.Reduce; // и методам result:=res; // адрес res возвращается в качестве значения функции end; { Удалять переменную res при выходе из функции нельзя, так как она содержит результат вычисления. } ... // реализация остальных методов производится аналогично end.
27
Опред ел ения м етод ов GetNum, GetDen и SetNum очев ид ны . Процед ура SetDen прежд е, чем ис пол ь з ов ат ь з начение, перед анное ей в качес т в е парам ет ра, пров еряет, уд ов л ет в оряет л и оноус л ов ию норм ал из ации. Е с л и нет — в оз бужд ает с я ис кл ючение. Пос кол ь ку в с е под програм м ы наход ят с я в м од ул е, с од ержащем опред ел ение кл ас с а, в с юд у д опус кает с я прям ое обращение к пол ям , нев з ирая на ихобл ат ь в ид им ос т и. Реал из ация процед ур Normalize, Reduce и DivideBy отл ичает с я от бол ее ранней в ерс ии тол ь ко з ам еной им ени перв ого парам ет ра на self. То, чтом ы им еем д ел ос объект ам и, а не с з апис ям и, прояв л яет с я л иш ь в реал из ации м етод а Slash. Т ип з начения э той функции опред ел ен как Rational, с л ед ов ат ел ь но она д ол жна в оз в ращат ь ад рес некоторого объект а наш его кл ас с а. Раз м ещение д анногообъект а в пам ят и произ в од ит инс т рукция res:=Rational.Create;
Тол ь ко пос л е э того с т анов ит с я в оз м ожной инициал из ация его пол ей и д ругие операции. Сл ед ует обрат ит ь ос обое в ним ание на то, чтоуд ал ят ь объект res поз ав ерш ении работ ы функции нел ь з я: он хранит рез ул ьт ат ы в ы чис л ений и д ол жен бы т ь перед ан в ы з ы в ающей програм м е. Пос л ед няя и д ол жна поз абот ит ь с я об ос в обожд ении пам ят и. В з акл ючение рас с м от рим прил ожение, д ем онс т рирующее работ у с объект ам и кл ас с а Rational и пред с т ав л яющее с обой прос тую м од ификацию програм м ы из § 1.2. Пример 2.6. Дем онс т рация операций, в ы пол няем ы хнад объект ам и кл ас с а Rational. program TestRational; {$APPTYPE CONSOLE} uses UnitRational; // подключаем модуль с определением класса var r1,r2,ra,rs,rm,rd: Rational; begin r1:=Rational.Create; // переменные r1 и r2 размещаются в памяти r2:=Rational.Create; // r1.SetNum(1); r1.SetDen(2); // переменным r1 и r2 присваиваются r2.SetNum(2); r2.SetDen(6); // значения 1/2 и 2/6 ra:=r1.Plus(r2); rs:=r1.Minus(r2); rm:=r1.Dot(r2); rd:=r1.Slash(r2); { Для объекта r1 вызываются методы-функции с адресом объекта r2 в качестве параметра. Возвращаемые ими ссылки на объекты,
28
содержащие результаты вычислений, присваиваются заранее объявленным, но не инициализированным переменным ra, rs, rm и rd. } WriteLn('Plus: ',ra.GetNum,'/',ra.GetDen); WriteLn('Minus: ',rs.GetNum,'/',rs.GetDen); WriteLn('Dot: ',rm.GetNum,'/',rm.GetDen); WriteLn('Slash: ',rd.GetNum,'/',rd.GetDen); ra.Add(r2); // rs.Subtract(r2); // теперь к объектам, созданным ранее, rm.MultiplyBy(r2); // применяются операторы с присвоением rd.DivideBy(r2); // WriteLn('Add: ',ra.GetNum,'/',ra.GetDen); WriteLn('Sub: ',rs.GetNum,'/',rs.GetDen); WriteLn('Mul: ',rm.GetNum,'/',rm.GetDen); WriteLn('Div: ',rd.GetNum,'/',rd.GetDen); r1.Destroy; r2.Destroy; { Функции Plus, Minus, Dot и Slash возвращают указатель на уже созданный объект, поэтому заранее размещать в памяти переменные ra, rs, rm и rd не требовалось. Однако уничтожить их по завершении программы необходимо. } ra.Destroy; rs.Destroy; rm.Destroy; rd.Destroy; ReadLn; end.
Зам ет им , чтов э той програм м е, в отл ичие от прив ед енной в § 1.2, непос ред с т в енное обращение к пол ям перем енны хуже нев оз м ожно, поэ том у в с юд у ис пол ь з уют с я в ы з ов ы м етод ов Get¼ и Set¼ Другим в ажны м отл ичием яв л яет с я яв ное с оз д ание и уд ал ение объектов , в том чис л е и т ех, которы е появ ил ис ь в процес с е работ ы под програм м м од ул я. Вообще, при работ е с функциям и, в оз в ращающим и с с ы л ки на объект ы , очень л егко д опус т ит ь ут ечку пам ят и. Рас с м от рим в качес т в е прим ера инс т рукцию sum:=r1.Plus(r2).Plus(r3);
гд е r1, r2, r3 и sum — объект ны е перем енны е кл ас с а Rational, причем r1, r2 и r3 уже с од ержат ад рес а реал ь ны хобъектов . И с пол ь з ов ание д анного в ы ражения не тол ь ко не яв л яет с я пред ос уд ит ел ь ны м , нод аже наоборот, поощряет с я объект ны м с инт акс ис ом . К объект у r1 прим еняет с я м етод Plus с парам ет ром r2, в оз в ращающий с с ы л ку на нов ы й объект кл ас с а Rational (наз ов ем егоtemp), с од ержащий с ум м у чис ел r1 и r2. Зат ем к объект у temp с нов а прим еняет с я м етод Plus с парам ет ром r3. Рез ул ьт атом егов ы пол нения яв л яет с я с с ы л ка еще на од ин объект в с е того же кл ас с а, которая и прис в аив ает с я перем енной sum.
29
Т ребуем ая операция произ в ед ена: sum ад рес ует объект, пред с т ав л яющий с обой с ум м у чис ел r1, r2 и r3. Н очтопроиз ош л ос объектом temp? Н ичего. Он по-прежнем у с ущес т в ует гд е-тов пам ят и, ноад рес егопот ерян. Т аким образ ом , програм м ис т в ы нужд ен в ы бират ь : л ибо не ис пол ь з ов ат ь с ов ерш енноес т ес т в енны е с цепки операторов , л ибом ирит ь с я с пос тоянной ут ечкой пам ят и. Н екоторы м ком пром ис с ом м ожет с л ужит ь м од ифициров анная в ерс ия оператора с прис в оением , пред с т ав л яющая с обой функцию, в оз в ращающую ад рес л ев огооперанд а: function Rational.Add(r:Rational): Rational; var _num,_den:Integer; begin _num:=self.num*r.den; _den:=self.den*r.num; self.num:=_num; self.den:=_den; self.Normalize; self.Reduce; result:=self; // в качестве результата возвращается // текущий объект end;
В этом с л учае в м ес то прив ед енной в ы ш е инс т рукции м ожно ис пол ь з ов ат ь в ы ражение sum.Add(r1).Add(r2).Add(r3);
пос л ед ов ат ел ь нод обав л яющее чис л а r1, r2 и r3 к sum. Зд ес ь , од нако, объект sum д ол жен бы т ь з аранее раз м ещен в пам ят и и инициал из иров ан нул ем , чтот акже не ув ел ичив ает чит абел ь нос т и програм м ы . Пос л е в с егос каз анногоу чит ат ел я м ожет с л ожит ь с я в печатл ение, что ут ечка пам ят и яв л яет с я неот ъем л ем ой чертой объект ного програм м иров ания. Э тоне т ак: с ущес т в уют ал горит м ы , поз в ол яющие ав том ат ичес ки уд ал ят ь объект ы , в которы хбол ь ш е нет необход им ос т и (этот процес с наз ы в ает с я «с боркой м ус ора» ). Под обны й ал горит м реал из ов ан, наприм ер, в яз ы ке Java.
2.5. К о нструкто ры и деструкто ры Н аряд у с опис анны м и в ы ш е с пос обам и д л я с оз д ания и уничтожения объектов м огут ис пол ь з ов ат ь с я с пециал ь ны е м етод ы : кон структоры и де структоры. Синт акс ичес ки они опред ел яют с я т ак же, как и обы чны е м етод ы -процед уры , тол ь коз арез ерв иров анное с л ов оprocedure з ам еняет с я на constructor ил и destructor. В час т нос т и, конс т рукторы и д ес т рукторы м огут им ет ь л юбой набор форм ал ь ны хпарам ет ров . Кром е в ы д ел ения и ос в обожд ения пам ят и, з аним аем ой объектом , 30
конс т рукторы и д ес т рукторы в ы пол няют т е же функции, что и с екции инициал из ации и финал из ации м од ул я. Конс т руктор з ад ает начал ь ны е з начения пол ей объект а и раз м ещает в пам ят и д инам ичес кие с т рукт уры д анны х. Дес т руктор ос в обожд ает рес урс ы , з анят ы е объектом в о в рем я егос ущес т в ов ания. Н ес м от ря на в неш ний в ид опред ел ения, конс т руктор в с егд а в оз в ращает ад рес с оз д анногоим объект а. Поэ том у д л я его в ы з ов а обы чно ис пол ь з ует с я в ы ражение ИмяОбъекта := ИмяКласса.ИмяКонструктора(Параметры);
Прив ед енная в ы ш е инс т рукция ИмяОбъекта := ИмяКласса.Create;
в д ейс т в ит ел ь нос т и яв л яет с я час т ны м с л учаем э того в ы ражения. Она раз м ещает объект пос ред с т в ом унас л ед ов анногоконс т руктора Create (о нас л ед уем ы хм етод ахс м . § 3.1). Вм ес т е с т ем , конс т руктор м ожет бы т ь в ы з в ан и при пом ощи обы чной объект ной с с ы л ки: ИмяОбъекта.ИмяКонструктора(Параметры);
В э том с л учае он не с оз д ает нов ы й объект, а оперирует с ущес т в ующим , в ы пол няя л иш ь т е инс т рукции, которы е перечис л ены в его(конс т руктора) реал из ации, и в оз в ращает ад рес указ анногообъект а. Вы з ов д ес т руктора ничем не отл ичает с я от в ы з ов а обы чногом етод а-процед уры : ИмяОбъекта.ИмяДеструктора(Параметры);
Как и в ы ш е, Destroy яв л яет с я прос тоим енем д ес т руктора, нас л ед уем ого кажд ы м кл ас с ом . Уничтожение объект а ид ет в поряд ке, обрат ном с оз д анию: с начал а в ы пол няют с я инс т рукции из реал из ации д ес т руктора, з ат ем ос в обожд ает с я пам ят ь , з аним аем аяобъект ом . Пос кол ь ку объект ы рас с м от ренного нам и кл ас с а Rational не ис пол ь з уют никакихраз д ел яем ы хрес урс ов , ос обы е д ес т рукторы д л я них не т ребуют с я. А в от хотя бы од ин с пециал ь ны й конс т руктор, наоборот, необход им , т ак как при с оз д ании нов огообъект а егоз нам енат ел ь с раз у хот ел ос ь бы ус т анав л ив ат ь бол ь ш им 0. Добав им к с екции public инт ерфейс а кл ас с а Rational объяв л ения constructor CreateNumber; constructor CreateAs(n, m: Integer);
а в раз д ел реал из ации м од ул я UnitRational с л ед ующий код : 31
constructor Rational.CreateNumber; begin self.num:=0; self.den:=1; end; constructor Rational.CreateAs(n, m: Integer); begin self.SetNum(n); self.SetDen(m); end;
Т еперь при ис пол ь з ов ании в ы ражения r := Rational.CreateNumber;
перем енная r пол учит ад рес объект а, пол я которого уже с од ержат коррект ны е в ел ичины 0 и 1. Е с л и же с оз д ать объект при пом ощи инс т рукции r := Rational.CreateAs(a, b);
тод л я инициал из ации чис л ит ел я и з нам енат ел я буд ут ис пол ь з ов аны м етод ы SetNum и SetDen, т ак чточис л оr окажет с я рав ны м a/b, л иш ь ес л и в ы ражения a и b им еют д опус т им ы е з начения. Кром е того, в ы з ы в ая конс т рукторы д л я уже с ущес т в ующегообъект а r: r.CreateNumber; r.CreateAs(a, b);
м ожнол егкос брос ит ь егоз начение в 0 ил и из м енит ь на т ребуем ое. Н екоторы м чит ат ел ям , в ероят но, покажет с я с т ранны м , что м ы прид ум ы в аем в с е нов ы е им ена д л я конс т рукторов в м ес то того, чтобы в ос пол ь з ов ат ь с я в ес ь м а ум ес т ной з д ес ь перегруз кой ил и ус т анов ит ь д л я парам ет ров n и m з начения поум ол чанию. Сд ел аноэтонам ереннос цел ь ю м акс им ал ь ноупрос т ит ь м ат ериал .
К с ожал ению, в нов ь д обав л енны е м етод ы не поз в ол яют реш ит ь в с ехпробл ем : пол ь з ов ат ел ь по-прежнем у м ожет с оз д ат ь нов ы й объект пос ред с т в ом обы чного конс т рукт ора Create, которы й не гарант ирует коррект нос т и его з начения. Ч т обы л икв ид иров ат ь т акую в оз м ожнос т ь , с л ед ует не прос то д обав ит ь к кл ас с у нов ы е конс т рукт оры , а переопред ел ит ь уже с ущес т в ующий. В д ейс т в ит ел ь нос т и э тоочень л егко: д ос т аточно л иш ь из м енит ь им я CreateNumber на Create, чт о м ы и с д ел аем , ус т ранив т ем с ам ы м пос л ед ний из нед ос т ат ков библ иот еки, перечис л енны хв начал е § 2.1. 32
Под робном еханиз м переопред ел ения м етод ов рас с м ат рив ает с я в с л ед ующем параграфе. У п раж нения 2.1. Доопред ел ит е кл ас с Rational, в кл ючив в негооператоры прис в аив ания и с рав нения. 2.2. Вз яв з а ос нов у библ иот еку из з ад ачи 1.1, опред ел ит е кл ас с д л я пред с т ав л ения в екторов д л ины n с цел ы м и ком понент ам и. Реал из уйт е операторы с л ожения, с кал ярного произ в ед ения, прис в аив ания, с рав нения на рав енс т в о, м етод ы д л я в ы чис л ения д л ины , чт ения и з апис и з начений ком понент. Н апиш ит е програм м у, д ем онс т рирующую работ у с объект ам и д анногокл ас с а. 2.3. Т а же з ад ача, но д л я м ат риц раз м ера n ´ n, эл ем ент ам и которы хяв л яют с я рационал ь ны е чис л а, т. е. объект ы рас с м от ренногокл ас с а Rational. Н еобход им ы е м ет од ы : с л ожение, ум ножение, прис в аив ание, с рав нение на рав енс т в о. У казание. Данном у кл ас с у необход им ы с пециал ь ны е конс т руктор и д ес т руктор. Перв ы й т ребует с я д л я раз м ещения в пам ят и объектов кл ас с а Rational, в торой — д л я ихуд ал ения. Обрат ит е т акже в ним ание на то, что м етод ы чт ения-з апис и эл ем ентов д ол жны копиров ат ь их з начения, а не ад рес а. В прот ив ном с л учае у пол ь з ов ат ел я появ л яет с я в оз м ожнос т ь неяв ногоиз м енения с од ержим огом ат рицы .
33
3. О Б ЪЕ К Т Н О -О РИ Е Н Т И РО В А Н Н О Е ПРО Г РА М М И РО В А Н И Е 3.1. Н аследование Как с л ожит ь д в е м ат рицы ? Дл я м ат ем ат ика от в ет на э т от в опрос очев ид ен: поэл ем ент но. Програм м ис т же, ис пол ь з ующий яз ы к с о с т рогой т ипиз ацией, в ы нужд ен с начал а уточнит ь , какой т ип им еют эл ем ент ы м ат риц. В с ам ом д ел е, при т рад иционном под ход е неяв ны е (ав том ат ичес кие) преобраз ов ания т ипов ограничив ают с я обы чно операциям и над с кал ярам и и некоторы м и прос т ейш им и прив ед ениям и. Поэ том у раз работ ат ь под програм м у, приним ающую в качес т в е аргум ентов м ат рицы раз л ичной природ ы , оказ ы в ает с я в ес ь м а з ат руд нит ел ь но. Как прав ил о, д л я кажд ой пары т ипов д анны хприход ит с я с оз д ав ат ь отд ел ь ную функцию, с нов а и с нов а пов торяя пос ут и од ин и тот же ал горит м . Н ет руд но в ид ет ь , что з ав ис им ос т ь кол ичес т в а под програм м от чис л а ис пол ь з уем ы хт ипов оказ ы в ает с я д л я бинарной операции д аже не л инейной, а кв ад рат ичной. Как с л ед с т в ие, програм м ис т (а без прим енения перегруз ки и пол ь з ов ат ел ь ) очень с короначинает тонут ь в э том м ногообраз ии. Кром е того, кл иент ы в ы нужд ены под д ержив ат ь пос тоянную обрат ную с в яз ь с раз работ чиком библ иот еки, пос кол ь ку они не в с егд а м огут ис пол ь з ов ат ь уже им еющиес я ал горит м ы д л я работ ы с нов ы м и, необход им ы м и им т ипам и д анны х. Объект но-ориент иров анны е яз ы ки пред ос т ав л яют в рас поряжение програм м ис т а с ред с т в а, поз в ол яющие д ос т аточно л егко раз рабат ы в ат ь обобщенны е ал горит м ы , прим еним ы е (причем без переком пил яции) к пот енциал ь но бес конечном у м ножес т в у раз л ичны хт ипов . Раз ум еет с я, э тот э ффект не м ожет бы т ь д ос т игнут без некоторого ос л абл ения конт рол я т ипов с ос тороны ком пил ятора, и в ел ичина т акогоос л абл ения оказ ы в ает с я очень в ажной характ ерис т икой яз ы ка. Од ной из нов ы хформ опред ел ения от нош ений м ежд у т ипам и, с очет ающей преим ущес т в а обоихпод ход ов , яв л яет с я раз работ ка ие ра рхии н а сл едова н ия. На сл едова н ием наз ы в ает с я м еханиз м , при котором од ин кл ас с опред ел яет с я на ос нов е д ругого, уже с ущес т в ующего. Ст ары й кл ас с наз ы в ают предком ил и ба зовым кл а ссом, нов ы й — потомком ил и производн ым кл а ссом. При од иночном нас л ед ов ании кажд ы й кл ас с им еет не бол ее од ного пред ка, поэ том у с ов окупнос т ь в с ех кл ас с ов и от нош ений м ежд у ним и образ ует д рев ов ид ную с т рукт уру, наз ы в аем ую иерархией нас л ед ов ания. 34
Е с т ес т в енно, баз ов ы й кл ас с в ы бирает с я не с л учайно, а т ак, чтобы кл ас с -потом ок с с од ержат ел ь ной точки з рения пред с т ав л ял с обой его под т ип. Т аким образ ом , от нош ение тожд ес т в а т ипов , ис пол ь з уем ое при т рад иционном под ход е д л я пров ерки коррект нос т и прис в аив ания, з ам еняет с я бол ее с л абы м от нош ением в кл ючения. Под робнее ос ов м ес т им ос т и кл ас с ов м ы буд ем гов орит ь в § 3.2. В процес с е нас л ед ов ания в с е чл ены баз ов огокл ас с а ав том ат ичес ки с т анов ят с я чл енам и произ в од ного, причем пос л ед ний м ожет переопред ел ит ь некоторы е из них, а т акже д обав ит ь нов ы е пол я, м етод ы и с в ойс т в а. Под переопред ел ением з д ес ь поним ает с я объяв л ение в кл ас с епотом ке чл ена с т ем же им енем , что и некоторы й чл ен кл ас с а-пред ка. Н икаких ограничений на переопред ел яем ы е чл ены не накл ад ы в ает с я, хотя в с л учае м етод ов нов ая и с т арая в ерс ия обы чноим еют од инаков ы е з агол ов ки. Объяв л ение произ в од ногокл ас с а в ы гл яд ит с л ед ующим образ ом : ИмяПотомка = class(ИмяПредка) СписокИзмененныхЧленов end;
гд е И м я П о т о м ка — л юбой д опус т им ы й ид ент ификатор, И м я П р едка — ид ент ификатор некоторогоопред ел енногоранее и д ос тупного в д анном м ес те кл ас с а, С пи с о кИ зм ененны хЧ лено в — с пис ок объяв л ений нов ы хи переопред ел енны хчл енов кл ас с а (в оз м ожно, пус той). По-прежнем у кажд ом у чл ену м ожет пред ш ес т в ов ать д ирект ив а, указ ы в ающая егообл ас т ь в ид им ос т и. При э том обл ас т ь в ид им ос т и чл ена, переопред ел яем ого в произ в од ном кл ас с е, не м ожет бы т ь с д ел ана м ень ш е, чем в баз ов ом . Кром е того, опубл иков анны е чл ены нел ь з я переопред ел ит ь как от кры т ы е. В яз ы ке Object Pascal кажд ы й кл ас с д ол жен им еть в точнос ти од ногопред ка. Е д инс тв енны м ис кл ючением из этогоправ ил а с л ужит в с т роенны й кл ас с TObject, пред с т ав л яющий с обой в ерш ину иерархии нас л ед ов ания. Вс е кл ас с ы , в объяв л ении которы хне указ аноим я пред ка, яв л яют с япрям ы м и потом кам и TObject, т ак чтов ы ражение ИмяКласса = class ... end;
рав нос ил ь нов ы ражению ИмяКласса = class(TObject) ... end;
И м енно от э того кл ас с а и унас л ед ов аны , в час т нос т и, упом инав ш иес я в ы ш е конс т руктор Create и д ес т руктор Destroy. В качес т в е прим ера рас с м от рим кл ас с д л я работ ы с p-ичны м и д робям и, тоес т ь рационал ь ны м и чис л ам и, з нам енат ел ь которы хяв л яет с я с т епень ю некоторогофикс иров анногопрос тогочис л а p.
35
Пример 3.1. И нт ерфейс кл ас с а PAryFraction. PAryFraction = class(Rational) // p-ичное рациональное число private // закрытые члены класса base: Integer; // простое число p protected // защищенные члены класса function isPrime(value: Integer): boolean; // Является простым function isPowerOfBase(value: Integer): boolean; // Является степенью base public // открытые члены класса constructor Create; constructor CreateAs(n, m: Integer); procedure SetDen(value: Integer); procedure SetBase(value: Integer); // Установить base function GetBase: Integer; // Вернуть base end;
Очев ид но, чтокажд ая p-ичная д робь характ ериз ует с я т рем я чис л ам и: чис л ит ел ем , з нам енат ел ем и прос т ы м чис л ом p, с т епень ю которогояв л яет с я з нам енат ел ь . Поэ том у к кл ас с у PAryFraction д обав л енонов ое пол е base (ос нов ание с т епени) и от кры т ы е м етод ы GetBase и SetBase д л я чт ения-з апис и его з начений. Пос кол ь ку чис л о, с охраняем ое в пол е base, с л ед ует обяз ат ел ь но пров ерят ь на прос тот у, в раз д ел protected в кл ючена с оот в ет с т в ующая функция isPrime. Дал ее, необход им о поз аботит ь с я о том , чтобы нов ое пол е в с егд а им ел о коррект ное з начение и з нам енат ел ь пред с т ав л ял с обой его с тепень . С э той цел ь ю кл ас с PAryFraction переопред ел яет конс т рукторы Create, CreateAs и м етод SetDen с в оего пред ка. Кром е того, к перечню з ащищенны хчл енов д обав л яет с я в с пом огател ь наяфункция isPowerOfBase. Обрат им с я т еперь к реал из ации нов ы хи переопред ел енны хм етод ов , пред пол агая, чтоопред ел ения с т арогои нов огокл ас с ов наход ят с я в раз ны хм од ул ях. В э том с л учае нам оказ ы в ают с я д ос т упны в с е чл ены баз ов огокл ас с а з а ис кл ючением з акры т ы х, тоес т ь в д анном конкрет ном с л учае з а ис кл ючением пол ей num и den, и з д ес ь в оз никает небол ь ш ая пробл ем а. Очев ид но, чтонов ая в ерс ия м етод а SetDen т ак ил и иначе д ол жна из м енят ь в ел ичину з нам енат ел я т екущегообъект а. Пос кол ь ку пол е den в произ в од ном кл ас с е нед ос т упно, ед инс т в енны й с пос об с д ел ат ь э то — ис пол ь з ов ат ь с т ары й в ариант той же процед уры , унас л ед ов анны й из кл ас с а Rational. Н опрос тое упом инание с обс т в енногоим ени в реал из ации м етод а SetDen буд ет в ос принятокак обы чны й рекурс ив ны й в ы з ов , что, раз ум еет с я, нам не под ход ит. 36
Дл я того, чтобы ком пил ятор м ог отл ичат ь с т ары е и нов ы е в ерс ии чл енов кл ас с а в яз ы к в в ед еноз арез ерв иров анное с л ов оinherited. Вы ражение inherited ИмяЧлена
гов орит о том , чтообращение произ в од ит с я к чл ену т екущего объект а, унас л ед ов анном у из непос ред с т в енного пред ш ес т в енника его кл ас с а в иерархии нас л ед ов ания. Указ ат ел ь self з д ес ь уже нед опус т им . Зам ет им , чтот аким образ ом м ожнопол учит ь д ос т уп л иш ь к чл енам бл ижайш егопред ка. Е с л и, с кажем , кл ас с C переопред ел ил м етод X кл ас с а B, а кл ас с B в с в ою очеред ь переопред ел ил с оот в ет с т в ующий м етод кл ас с а A, тов ы з в ат ь в ерс ию м етод а X д л я кл ас с а A при реал из ации кл ас с а C нев оз м ожно. И т ак, рас с м от рим Пример 3.2. Реал из ация кл ас с а PAryFraction. unit UnitPAryFraction; interface uses UnitRational; // подключаем модуль, // в котором реализован класс Rational type PAryFraction = class(Rational) // здесь находится приведенный выше интерфейс класса end; implementation uses SysUtils; // модуль включает поддержку исключений function PAryFraction.isPrime(value: Integer): boolean; var i:Integer; begin if value<2 then begin result:=false; exit; end; result:=true; i:=1; repeat inc(i); // i изменяется от 2 и максимум до value/2+1 if ((value mod i)=0) and (i=(value div 2)); end; function PAryFraction.isPowerOfBase(value: Integer): boolean; var v:Integer; begin
37
if value=0 then raise Exception.Create('Ambiguous result'); { Если value равно нулю, возбуждается исключение с сообщением "Неоднозначный результат". } v:=value; while (v mod self.base)=0 do v:=v div self.base; if (v=1) or (v=-1) then result:=true else result:=false; { Делим value на base до тех пор, пока это возможно. Если в результате остается ±1, значит value является степенью base. } end; function PAryFraction.GetBase: Integer; begin result:=self.base; end; procedure PAryFraction.SetBase(value: Integer); begin if (value<>self.base) then if self.isPrime(value) then // value должно быть простым begin self.base:=value; self.SetNum(0); self.SetDen(1); end else // иначе возбуждается исключение с сообщением // "Недопустимое значение основания" raise Exception.Create('Illegal value of base'); end; procedure PAryFraction.SetDen(value: Integer); begin if (value>0) and self.isPowerOfBase(value) then inherited SetDen(value) else raise Exception.Create('Illegal value of denominator'); { Если передаваемое значение положительно и является степенью base, то можно изменить знаменатель, вызвав старую версию метода SetDen. Иначе возбуждается исключение с сообщением "Недопустимое значение знаменателя". } end; constructor PAryFraction.Create; begin inherited Create; // старый конструктор инициализирует поля num и den self.base:=2; end;
38
constructor PAryFraction.CreateAs(n, m: Integer); var i: Integer; begin if m<1 then // знаменатель должен быть положителен raise Exception.Create('Illegal value of denominator'); if m=1 then begin // определить base невозможно, полагаем равным 2 self.base:=2; self.SetNum(n); self.SetDen(m); exit; end; self.base:=0; i:=1; repeat // выбираем первый простой делитель числа m inc(i); if (m mod i)=0 then self.base:=i; until self.base=i; self.SetNum(n); self.SetDen(m); // если m не является степенью base, SetDen вызовет исключение end; end.
Реал из ация м етод ов isPrime , isPowerOfBase и GetBase очев ид на. Процед ура SetBase в ы пол няет с я, л иш ь когд а перед ав аем ое ей з начение отл ично от т екущего. Пос кол ь ку з нам енат ел ь д ол жен бы т ь с т епень ю base, его в э т ом с л учае т акже необход им ом енят ь , и ед инс т в енны й раз ум ны й в ы ход — ус т анов ит ь в с е чис л орав ны м 0. М етод SetDen пров еряет, яв л яет с я л и нов ое з начение д опус т им ы м и, ес л и д а, в ы з ы в ает с в ою с т арую в ерс ию, которая и м од ифицирует з начение пол я. Анал огичны м образ ом реал из ов ан конс т руктор Create, инициал из ирующий унас л ед ов анны е пол я при пом ощи конс т руктора кл ас с а Rational. Н аконец, конс т руктор CreateAs пы т ает с я опред ел ит ь з начение base поз нам енат ел ю. В качес т в е канд ид ат а в ы бирает с я перв ы й же прос той д ел ит ел ь чис л а m, ес л и оноотл ичноот ед иницы , и 2 в прот ив ном с л учае. Зав ерш ает пров ерку коррект нос т и м етод SetDen (нов огокл ас с а), в ы з ы в аем ы й д ал ее. Зам ет им , чтоз ам енит ь в ы ражение self.SetNum(n); self.SetDen(m);
в реал из ации д анногоконс т руктора инс т рукцией inherited CreateAs(n, m);
нел ь з я, т ак как с т ары й конс т руктор в ы з ов ет с т арую же в ерс ию м етод а SetDen и пров ерка буд ет в ы пол нена не в пол ном объем е.
39
3.2. Совместимо сть классов и ее о со б енно сти При пос т роении произ в од ногокл ас с а в пред ы д ущем параграфе м ы не в нес л и никакихиз м енений в с пис ок м етод ов , ос ущес т в л яющихарифм ет ичес кие операции, и т акое пол ожение д ел в ряд л и м ожнос чит ат ь уд ов л ет в орит ел ь ны м . Пос кол ь ку м ножес т в оp-ичны хд робей з ам кнутоот нос ит ел ь нос л ожения, в ы чит анияи произ в ед ения, в пол не ес т ес тв еннобы л о бы им еть в нов ом кл ас с е операторы , приним ающие и в оз в ращающие в качес тв е з начений p-ичны е д роби, тоес т ь объекты кл ас с а PAryFraction. Н а перв ы й в з гл яд им еют с я д в а реш ения э той пробл ем ы : л ибопереопред ел ит ь уже им еющиес я м етод ы с л ед ующим образ ом : function Plus(r: PAryFraction): PAryFraction; procedure Add(r: PAryFraction); ...
л ибо прос то д обав ит ь нов ы е операторы , под обрав д л я нихпод ход ящие им ена. И то, и д ругое неуд ачно: в перв ом с л учае м ы т еряем в оз м ожнос т ь с м еш ив ат ь в в ы чис л енияхp-ичны е д роби и произ в ол ь ны е рационал ь ны е чис л а, в о в тором — ос л ожняем работ у пол ь з ов ат ел я, напол няя инт ерфейс кл ас с а м етод ам и, реал из ующим и почт и од инаков ы е операции. В д ейс т в ит ел ь нос т и им еет с я и т рет ий в ариант. Т ребуем ого эффект а поз в ол яет д обит ь с я прос тое переопред ел ение с ущес т в ующихоператоров без какого-л ибо из м енения их з агол ов ков . Т акая в оз м ожнос т ь появ л яет с я бл агод аря том у, что объект ы кл ас с а-потом ка совме стимы в отн ош е н ии опе ра ции присва ива н ия с объект ам и кл ас с а-пред ка. Зд ес ь необход им ов с пом нит ь , чтот ип A наз ы в ает с я с ов м ес т им ы м (assignment-compatible) с т ипом B, ес л и: 1) перем енной т ипа B м ожет бы т ь прис в оеноз начение т ипа A; 2) в под програм м у, форм ал ь ны й парам ет р которой им еет т ип B, м ожет бы т ь перед аноз начение т ипа A. Ос ущес т в л ят ь с я с ов м ес т им ос ть м ожет д в ум я раз л ичны м и с пос обам и, которы е м ы буд ем наз ы в ать преобра зова н ием и приведе н ием т ипов . При преобраз ов ании м аш инное (битов ое) пред с т ав л ение перед ав аем ого з начения под в ергает с я некотором у реал ь ном у из м енению, наприм ер, ув ел ичению з аним аем огоим объем а пам ят и с з апол нением с в обод ного прос т ранс т в а нул ям и ил и ед иницам и. Т акие операции характ ерны д л я переход ов м ежд у цел очис л енны м и и в ещес т в енны м и пред с т ав л ениям и чис ел раз л ичной точнос т и. Прив ед ение т ипа никак не из м еняет м аш инную форм у д анны х, поэ том у его м ожно наз в ат ь т акже ре ин те рпре та цие й им еющейс я ин40
форм ации. Т ак, наприм ер, прив ед ение с с ы л ок поз в ол яет ком пил ятору инт ерпрет иров ат ь од ин и тот же указ ат ел ь как ад рес чегоугод но: чис л а, с им в ол а и т. д . Сов м ес т им ос т ь кл ас с ов реал из ует с я им енно пут ем прив ед ения. Кажд ая объект ная перем енная пред с т ав л яет с обой обы чны й указ ат ел ь , и, в ы пол няя нес л ожны е м анипул яции, м ы м ожем с опос т ав ит ь ей ад рес объект а л юбогокл ас с а, а не тол ь котого, которы й фигуриров ал в объяв л ении д анной перем енной. Од нако под обная в с ед оз в ол еннос т ь чрев ат а с ерь ез ны м и ош ибкам и, прояв л яющим ис я тол ь ков ов рем я в ы пол нения програм м ы . В с ам ом д ел е, ес л и м ы прис в оим перем енной кл ас с а PAryFraction ад рес некоторогообъект а r кл ас с а Rational, тооперации с пол ем base в ы в ед ут нас з а пред ел ы реал ь ногообъект а. В час т нос т и, егоиз м енение прив ед ет к неяв ной м од ификации д анны х, хранящихс я в пам ят и с раз у з а объектом r (с м . рис . 3.1).
пол е num пол е den
Объект r
Ад рес уем ы й объект
Сс ы л ка на объект кл ас с а PAryFraction
пол е base
Рис . 3.1. И м еннопоэ том у прив ед ение т ипа, ав том ат ичес ки ос ущес т в л яем ое ком пил ятором , ограничив ает с я прис в аив анием с с ы л ке на объект кл ас с апред ка ад рес а объект а кл ас с а-потом ка. Пос кол ь ку л юбой чл ен баз ов ого кл ас с а яв л яет с я и чл еном произ в од ного, реал ь ны й объект в этом с л учае з ав ед ом ос од ержит в с е пол я, к которы м д опус т им ообращение (и раз м ещают с я они точнот ак же, как и в объект ахбаз ов огокл ас с а). Кром е того, в с е операции, прим еним ы е к э кз ем пл ярам кл ас с а-пред ка, оказ ы в ают с я в ы пол ним ы и д л яэ кз ем пл яров кл ас с а-потом ка (с м . рис . 3.2). Т аким образ ом , програм м ис т пол учает без опас ную в оз м ожнос т ь м анипул иров ат ь объект ам и произ в од ны хкл ас с ов при пом ощи с с ы л ок на объект ы кл ас с а баз ов ого. С д ругой с тороны , с л ед ует пос тоянноим ет ь в в ид у, что ис пол ь з уем ы й объект м ожет принад л ежат ь л юбом у кл ас с у из некоторого пот енциал ь но бес конечного с ем ейс т в а, опред ел яем ого иерархией нас л ед ов ания. И м енно э т им объе ктн о-орие н тирова н н ое про41
грам м иров ание отл ичает с я от объе ктн ог о, гд е т ип кажд ой перем енной од ноз начноопред ел ен на э т апе раз работ ки.
PAryFraction
Объект кл ас с а
пол е num пол е den
Ад рес уем ы й объект
Сс ы л ка на объект кл ас с а Rational
пол е base
Рис . 3.2. И з с каз анногов ы ш е с т анов ит с я понятно, почем у объект ная перем енная с од ержит не с ам объект, а л иш ь с с ы л ку на него. В против ном с л учае ее раз м ер опред ел ял с я бы кл ас с ом , указ анны м при объяв л ении, и раз м ес т ит ь в этой обл ас ти пам ят и экз ем пл яр произ в ол ь ногокл ас с а-потом ка, не в ы пол няя никакихпреобраз ов аний, бы л обы с корее в с егонев оз м ожно.
3.3. О п ределение тип а о б ъекта во времявы п о лнения Вернем с я т еперь с нов а к наш ем у прим еру. М ы в ы яс нил и, чтоуточнит ь операторы в кл ас с е PAryFraction м ожно, прос то переопред ел ив с оот в ет с т в ующие м етод ы без из м енения их объяв л ений. Опиш ем под робно, как они т еперь д ол жны в ы пол нят ь с я. Е с л и перед ав аем ы й в качес т в е парам ет ра r объект в д ейс т в ит ел ь нос т и принад л ежит кл ас с у PAryFraction (ил и од ном у из его нас л ед ников ), нам прежд е в с его необход им о убед ит ь с я в том , что он и т екущий объект им еют од ноос нов ание. В с л учае д ел ения с л ед ует пров ерит ь т акже, яв л яет с я л и чис л ит ел ь r с т епень ю пол я base. Е с л и и то, и д ругое в ерно, операция в ы пол ним а и рез ул ьт ат ее им еет т ип PAryFraction, иначе с ит уация оказ ы в ает с я неод ноз начной и ед инс т в енны й в ы ход — с генериров ат ь ис кл ючение. Пред пол ожим т еперь , чтот ип объект а r не принад л ежит под д ерев у, порожд аем ом у в иерархии нас л ед ов ания кл ас с ом PAryFraction. В э том с л учае м ы не рас пол агаем никакой информ ацией об r з а ис кл ючением той, которую д ает кл ас с Rational, поэ том у и рез ул ьт атом в ы чис л ений оказ ы в ает с я объект кл ас с а Rational. Данная с ит уация нед опус т им а д л я операторов с прис в оением , пос кол ь ку они с охраняют пол ученное чис л о в т екущем объект е, а пред с т ав ит ь произ в ол ь ное рационал ь ное 42
чис л ов в ид е p-ичной д роби нел ь з я. Т аким образ ом , нам с нов а не ос т ает с я ничегол учш его, как в оз буд ит ь ис кл ючение. Зам ет им , чтопов ед ение функций Plus и Dot в с л учае, когд а ихаргум ент не принад л ежит кл ас с у PAryFraction, в д ейс т в ит ел ь нос т и пол нос т ь ю опред ел яет с я реал из ацией анал огичны хм етод ов кл ас с а Rational. В с ам ом д ел е, операции с л ожения и ум ножения в пол е Q ком м ут ат ив ны , поэтом у рез ул ьт ат ихприм енения к паре объектов , од ин ихкоторы хяв л яет с я p-ичной д робь ю, а д ругой — нет, не д ол жен з ав ис ет ь от поряд ка аргум ентов . Н оес л и на перв ом м ес т е с тоит обы чное рационал ь ное чис л о, то д л я в ы пол нения в ы чис л ений ис пол ь з ует с я в ерс ия м етод а из кл ас с а Rational, д л я которой т ип второгоаргум ент а уже не им еет никакогоз начения.
И т ак, ос нов ной в опрос , ос т ающийс я от кры т ы м : как опред ел ит ь реал ь ны й т ип объект а в ов рем я в ы пол нения програм м ы ? В Object Pascal д л я э тогоис пол ь з ует с я оператор is. Вы ражение ИмяОбъекта is ИмяКласса
в оз в ращает «ис т ину» , ес л и объект принад л ежит д анном у кл ас с у ил и од ном у из егопотом ков и «л ожь » в прот ив ном с л учае. Дл я ис пол ь з ов ания операт ора is необход им о, чтобы объект и кл ас с бы л и с рав ним ы , т. е. форм ал ь ны й т ип объект ной перем енной, указ анны й при ее объяв л ении, яв л ял с я л ибопред ком , л ибопотом ком кл ас с а, с которы м произ в од ит с я с рав нение. И ное не д опус т ит ком пил ятор. Пос л е опред ел ения кл ас с а объект а необход им о в ы пол нит ь с оот в ет с т в ующее прив ед ение т ипа. Сд ел ат ь э то м ожно как пос ред с т в ом обы чной инс т рукции ИмяКласса(ИмяОбъекта)
т ак и при пом ощи с пециал ь ного оператора as. Пос л ед ний отл ичает с я т ем , что пом им о с обс т в енно прив ед ения т ипа ос ущес т в л яет пров ерку коррект нос т и э той операции. Вы ражение ИмяОбъекта as ИмяКласса
поз в ол яет ис пол ь з ов ат ь объект ную перем енную как с с ы л ку на объект указ анногокл ас с а (как и в ы ш е, з д ес ь необход им а с рав ним ос т ь объект а и кл ас с а). Е с л и при э том т ип реал ь ного объект а не с ов пад ает с д анны м кл ас с ом ил и его потом ком (т. е. в ы ражение И м я О бъект а is И м я Клас с а л ожно), в оз никает с оот в ет с т в ующее ис кл ючение. Т аким образ ом , оператор as в ы пол няет без опас ное прив ед ение т ипа, гарант ируя пред с каз уем ое пов ед ение програм м ы при л юбом с очет ании д ейс т в ит ел ь ного и т ребуем ого кл ас с ов объект а. Т ем не м енее, 43
прим енят ь егобез пред в арит ел ь ногов ы з ов а оператора is с л ед ует л иш ь в том с л учае, когд а в ероят нос т ь ус пеш ногорез ул ьт ат а д ос т аточнов ы с ока. Вс е опис анны е в ы ш е с ред с т в а ис пол ь з ует Пример 3.3. Реал из ация операций в кл ас с е PAryFraction. procedure PAryFraction.Add(r: Rational); begin if (r is PAryFraction) and ((r as PAryFraction).base=self.base) then inherited Add(r) { Если r содержит адрес объекта класса PAryFraction и основание этого объекта совпадает с текущим, то сложение допустимо и выполняется посредством старой версии метода. } else raise Exception.Create('Invalid operation'); { Иначе возбуждается исключение с сообщением "Недопустимая операция". } end; procedure PAryFraction.DivideBy(r: Rational); begin if (r is PAryFraction) and ((r as PAryFraction).base=self.base) and self.isPowerOfBase(r.GetNum) then inherited DivideBy(r) { Дополнительно проверяется, является ли числитель r степенью основания. } else raise Exception.Create('Invalid operation'); end; function PAryFraction.Plus(r: Rational): Rational; var res: PAryFraction; begin if (r is PAryFraction) then if (r as PAryFraction).base=self.base then begin res:=PAryFraction.CreateAs(self.GetNum, self.GetDen); res.base:=self.base; res.Add(r); // вызов определенного выше метода Add result:=res; end else raise Exception.Create('Invalid operation') { Если объект r принадлежит классу PAryFraction и имеет то же основание, что и текущий, то создается копия текущего объекта,
44
к которой и добавляется r. Полученная в итоге сумма чисел self и r возвращается в качестве результата функции. } else result:=inherited Plus(r); { Если объект r не принадлежит классу PAryFraction, выполняется обычное сложение посредством старой версии метода, возвращающей в качестве результата объект класса Rational. } end; function PAryFraction.Slash(r: Rational): Rational; var res: PAryFraction; begin if (r is PAryFraction) then if ((r as PAryFraction).base=self.base) and self.isPowerOfBase(r.GetNum) then begin { Дополнительно проверяется, является ли числитель r степенью основания. } res:=PAryFraction.CreateAs(self.GetNum, self.GetDen); res.base:=self.base; res.DivideBy(r); // вызов определенного выше метода DivideBy result:=res; end else raise Exception.Create('Invalid operation') else result:=inherited Slash(r); end; ... // реализация остальных методов производится аналогично
3.4. Статическо е и динамическо е связы вание Попробуем т еперь прив ес т и прос т ейш ий прим ер обобщенного ал горит м а. Опред ел им т ип Matrix с л ед ующим образ ом : Matrix = array[1..n,1..n] of Rational;
гд е n — некоторая цел очис л енная конс т ант а, бол ь ш ая 1, и рас с м от рим function AddMatrix(m1, m2: Matrix): Matrix; var i,j: Integer; begin for i:=1 to n do for j:=1 to n do result[i,j]:=m1[i,j].Plus(m2[i,j]); end;
45
Э т а функция с кл ад ы в ает м ат рицы m1 и m2, в ы з ы в ая д л я кажд ого эл ем ент а перв ой м ат рицы м етод Plus и перед ав ая ем у в качес т в е аргум ент а с оот в ет с т в ующий эл ем ент в торой м ат рицы . В рез ул ь т ат е пол учает с я м ат рица, с од ержащая с с ы л ки на объект ы , с оз д анны е в процес с е работ ы м етод ов Plus. М ы з наем , что хотя форм ал ь но эл ем ент ы м ат риц принад л ежат кл ас с у Rational, в д ейс т в ит ел ь нос т и они м огут с од ержат ь с с ы л ки и на объект ы л юбого д ругого кл ас с а, произ в од ного от Rational, наприм ер, PAryFraction. Е с л и т еперь при в ы з ов е м етод а Plus егов ерс ия т акже буд ет в ы бират ь с я ис ход я из реал ь ного, а не форм ал ь ного, т ипа эл ем ент а м ат рицы , то м ы пол учим обобщенны й ал горит м : рез ул ь т атом с л ожения обы чны храционал ь ны хчис ел буд ет рационал ь ное чис л о, p-ичны хд робей — p-ичная д робь и т. д . Пос м от рим , как им еннообс тоит д ел о. Пример 3.4. Програм м а, д ем онс т рирующая работ у обобщенного ал горит м а. program TestAlgoritm; {$APPTYPE CONSOLE} uses UnitRational, // необходим для объявления типа Matrix UnitPAryFraction; // необходим для создания реальных объектов const n = 3; type Matrix = array[1..n,1..n] of Rational; function AddMatrix(m1, m2: Matrix): Matrix; ... // здесь находится тело функции, приведенное выше procedure WriteMatrix(m: Matrix); var i,j:Integer; begin for i:=1 to n do begin for j:=1 to n do begin Write(m[i,j].GetNum,'/', m[i,j].GetDen); if (m[i,j] is PAryFraction) then Write(' (PAryFraction), ') else Write(' (Rational), '); end; // вместе со значением каждого элемента выводится WriteLn; // информация о его типе: Rational или PAryFraction end; WriteLn; end;
46
var m1,m2,m3: Matrix; i,j: Integer; begin for i:=1 to n do for j:=1 to n do begin m1[i,j]:=PAryFraction.CreateAs(1,(2 shl i)); m2[i,j]:=PAryFraction.CreateAs(1,(2 shl j)); end; { Элементам матриц присваиваются ссылки на объекты класса PAryFraction. } m3:=AddMatrix(m1,m2); // вызывается реализация алгоритма сложения WriteMatrix(m1); // WriteMatrix(m2); // содержимое матриц выводится на экран WriteMatrix(m3); // for i:=1 to n do for j:=1 to n do begin m1[i,j].Destroy; m2[i,j].Destroy; m3[i,j].Destroy; end; { По окончании работы программы необходимо удалить как элементы матриц m1 и m2, так и созданные в процессе работы методов Plus элементы матрицы m3. } ReadLn; end.
Прив ед енная програм м а с кл ад ы в ает д в е м ат рицы 2-ичны х д робей, с од ержащие с л ед ующие з начения: æ 14 14 14 ö æ ç1 1 1÷ ç ç8 8 8÷ и ç ç1 1 1÷ ç è 16 16 16 ø è
1 4 1 4 1 4
1 8 1 8 1 8
1 ö 16 1 ÷ 16 ÷ 1 ÷ 16 ø
(в ы ражение 2 shl k ос ущес т в л яет с д в иг битов огопред с т ав л ения чис л а 2 на k бит в л ев о, что э кв ив ал ент ноего ум ножению на 2k ). Пол ученная в рез ул ь т ат е м ат рица m3 опред ел ена коррект но: æ 12 83 165 ö ç3 1 3÷ ç 8 4 16 ÷ ç5 3 1÷ è 16 16 8 ø но, ув ы , в с е ее эл ем ент ы принад л ежат кл ас с у Rational, а не PAryFraction. Т аким образ ом , в процес с е работ ы функции AddMatrix реал ь ны й т ип объектов бы л проигнориров ан и в с юд у ис пол ь з ов ал ас ь в ерс ия м етод а Plus из кл ас с а Rational. 47
Объяс нение д анной с ит уации з акл ючает с я в том , что в с е чл ены кл ас с а поум ол чанию яв л яют с я ста тиче скими. Э то, в час т нос т и, оз начает, что ад рес а м етод ов в ы чис л яют с я в о в рем я ком пил яции в з ав ис им ос т и от форм ал ь ного т ипа объект ной перем енной, д л я кот орой они в ы з ы в ают с я. Раз ум еет с я, в рас с м от ренном конкрет ном с л учае реш ит ь пробл ем у с функцией AddMatrix м ожно, д обав ив в ее реал из ацию опред ел ение и прив ед ение т ипов при пом ощи операторов is и as. Од наконабор кл ас с ов , которы м м ожет принад л ежат ь эл ем ент м ат рицы , окажет с я при э том фикс иров ан на э т апе раз работ ки и, в час т нос т и, конечен. И з бав ит ь с я от д анногоограничения поз в ол яет м еханиз м дин а миче ског о связыва н ия м етод ов . Е го прим енение поз в ол яет в пол ной м ере реал из ов ат ь пол иморфизм объектов , т. е. в оз м ожнос т ь ав том ат ичес кого м анипул иров ания объект ам и раз ны хт ипов при пом ощи с с ы л ок на баз ов ы й кл ас с . Е с л и д л я м етод а ис пол ь з ует с я д инам ичес кое с в яз ы в ание, то ад рес конкрет ной под програм м ы , которую необход им ов ы з в ат ь , опред ел яет с я прям ов ов рем я в ы пол нения програм м ы , ис ход я из реал ь ногот ипа объект а. И м енноэ тогоэффект а м ы и хот ел и д обит ь с я, с оз д ав ая функцию AddMatrix. М еханиз м д инам ичес кого с в яз ы в ания прим еним тол ь ко к м етод ам , с с ы л ки на пол я объект а в с егд а опред ел яют с я с т ат ичес ки. Е с л и произ в од ны й кл ас с переопред ел яет пол я баз ов ого, указ ы в ая д л я нихнов ы й т ип, тообъект ы кл ас с а-потом ка буд ут с од ержат ь оба экз ем пл яра пол я и в ы бор того ил и иного ос ущес т в л яет с я в о в рем я ком пил яции, ис ход я из форм ал ь ногот ипа ис пол ь з уем ой объект ной перем енной.
Дл я в кл ючения м еханиз м а д инам ичес когос в яз ы в ания поот нош ению к том у ил и ином у м етод у необход им о, чтобы в кл ас с е, гд е э тот м етод в перв ы е опред ел ен, пос л е его объяв л ения бы л о указ ано з арез ерв иров анное с л ов оvirtual (виртуа л ьн ый). Кром е того, при переопред ел ении д анногом етод а в ов с ехкл ас с ах-нас л ед никахс оот в ет с т в ующее объяв л ение с л ед ует з ав ерш ат ь з арез ерв иров анны м с л ов ом override (за ме щ а ю щий). Виртуал ь ны м и м огут бы т ь объяв л ены л юбы е м етод ы , в кл ючая конс т рукторы и д ес т рукторы , с л ед ует тол ь ко от м ет ит ь , что з агол ов ки в иртуал ь ногои з ам ещающихм етод ов д ол жны с ов пад ат ь в точнос т и. Н а с ам ом д ел е д инам ичес кое с в яз ы в ание м ожет ис пол ь з ов ат ь с я д л я м етод а и в пред ел ахнекоторой цепочки кл ас с ов , опред ел яем ой иерархией нас л ед ов ания. Пус т ь , наприм ер, им еют с я кл ас с ы C1, C2, ¼, Cn, и кл ас с Ci + 1 яв л яет с я произ в од ны м от Ci д л я кажд огоi < n. Пус т ь т акже в с е кл ас с ы Ci с од ержат од инаков ое объяв л ение м етод а X, причем в некото-
48
ром кл ас с е Ck он объяв л ен как в иртуал ь ны й, а в ов с ехкл ас с ахCi, k < i £ l, (l £ n) — как з ам ещающий. Тогд а при в ы з ов е м етод а X прим еняет с я д инам ичес кое с в яз ы в ание, л иш ь ес л и форм ал ь ны й т ип ис пол ь з уем ой объект ной перем енной принад л ежит цепочке Ck – Ck + 1 – ¼ – Cl.
И т ак, д л я коррект ной работ ы наш егообобщенногоал горит м а необход им о д опол нит ь инт ерфейс ы кл ас с ов Rational и PAryFraction с оот в ет с т в ующим и д ирект ив ам и: Rational = class ... function Plus(r:Rational): Rational; function Minus(r:Rational): Rational; function Dot(r:Rational): Rational; function Slash(r:Rational): Rational; procedure Add(r:Rational); procedure Subtract(r:Rational); procedure MultiplyBy(r:Rational); procedure DivideBy(r:Rational); end; PAryFraction = class(Rational) ... function Plus(r:Rational): Rational; function Minus(r:Rational): Rational; function Dot(r:Rational): Rational; function Slash(r:Rational): Rational; procedure Add(r:Rational); procedure Subtract(r:Rational); procedure MultiplyBy(r:Rational); procedure DivideBy(r:Rational); end;
virtual; virtual; virtual; virtual; virtual; virtual; virtual; virtual;
override; override; override; override; override; override; override; override;
Т еперь в ы з ов той же с ам ой д ем онс т рационной програм м ы из прим ера 3.4 прив од ит к жел аем ом у рез ул ьт ату: в с е эл ем ент ы м ат рицы m3 оказ ы в ают с я объект ам и кл ас с а PAryFraction. Зам ет им , что д л я в кл ючения м еханиз м а д инам ичес кого с в яз ы в ания м ы д ол жны в нос ит ь из м енения в о в с е кл ас с ы иерархии нас л ед ов ания, с од ержащие инт ерес ующий нас м етод . Пос кол ь ку эт и кл ас с ы м огут раз рабат ы в ат ь с я с ов ерш енно раз ны м и л юд ь м и, под обная м од ификация не в с егд а ос ущес т в им а. Почем у же тогд а в с е м етод ы не объяв л яют с я в иртуал ь ны м и ав том ат ичес ки? Пробл ем а з акл ючает с я в с нижении бы с т род ейс т в ия при ис пол ь з ов ании ал горит м а д инам ичес когос в яз ы в ания. Кажд ы й в ы з ов в ирт уал ь -
49
ного м етод а т ребует по крайней м ере од ного д опол нит ел ь ного обращения к пам ят и (чаще д в ух). Т ем не м енее, д обав л яем ы е к кл ас с у нов ы е м етод ы реком енд ует с я объяв л ят ь в иртуал ь ны м и, ес л и ес т ь хот ь небол ь ш ая в ероят нос т ь ихпереопред ел ения в кл ас с ах-нас л ед никах.
В з акл ючение прив ед ем еще од ин, чут ь м енее т рив иал ь ны й, прим ер функции д л я ум ножения д в ухм ат риц, объед инив ее в м ес т е с опред ел ениям и конс т ант ы n, т ипа Matrix и функции AddMatrix в отд ел ь ном м од ул е UnitAlgorithms. П р им ер 3.5. М од ул ь , с од ержащий обобщенны е ал горит м ы д л я работ ы с
м ат рицам и. unit UnitAlgorithms; interface uses UnitRational; // необходим здесь для определения типа Matrix const n = 3; type Matrix = array[1..n,1..n] of Rational; function AddMatrix(m1, m2: Matrix): Matrix; function MultiplyMatrix(m1, m2: Matrix): Matrix; implementation function AddMatrix(m1, m2: Matrix): Matrix; ... // здесь находится тело функции, приведенное в начале параграфа function MultiplyMatrix(m1, m2: Matrix): Matrix; var i,j,k: Integer; p: Rational; begin for i:=1 to n do for j:=1 to n do begin result[i,j]:=m1[i,1].Dot(m2[1,j]); for k:=2 to n do begin p:=m1[i,k].Dot(m2[k,j]); // функция Dot возвращает новый объект, result[i,j].Add(p); // после добавления к элементу p.Destroy; // матрицы result он больше не нужен end; end; end; end.
Ф ункция MultiplyMatrix перем ножает м ат рицы m1 и m2, пос л ед ов ат ел ь но с кл ад ы в ая произ в ед ения ихэл ем ентов пос ред с тв ом м етод а Add. 50
При ум ножении с т роки м ат рицы m1 на с тол бец м ат рицы m2 м етод Dot с оз д ает n нов ы хобъектов кл ас с а Rational, из которы хл иш ь од ин ис пол ь з ует с я как эл ем ент рез ул ьт ирующей м ат рицы . Ос т ал ь ны е, пос л е д обав л ения с од ержащихся в нихз начений, д ол жны бы ть уд ал ены пут ем яв ного в ы з ов а д ес т руктора. 3.5. А б страктны е методы и классы От м ет им , что в под програм м ахиз м од ул я UnitAlgorithms ис пол ь з уют с я л иш ь м етод ы кл ас с а Rational, ос ущес т в л яющие арифм ет ичес кие операции. Э то не с л учайно: д л я реал из ации ос нов ны хал горит м ов л инейной ал гебры в пол не д ос т аточно, чтобы эл ем ент ы м ат риц принад л ежал и некотором у кол ь цу, т. е. абс т ракт ном у м ножес т в у с операциям и с л ожения, в ы чит ания и ум ножения. Учит ы в ая д анное з ам ечание, пос т араем с я нес кол ь ко ув ел ичит ь обл ас т ь прим енения функций AddMatrix и MultiplyMatrix. Дл я э того нам д ос т аточнобуд ет с д ел ат ь д в е в ещи: д обав ит ь в ос нов у им еющейс я иерархии нас л ед ов ания нов ы й кл ас с , с кажем Number, д л я пред с т ав л ения эл ем ентов произ в ол ь ного кол ь ца и м од ифициров ат ь м од ул ь UnitAlgorithms т аким образ ом , чтобы он ис пол ь з ов ал тол ь ко объект ы д анного кл ас с а. И нт ерфейс кл ас с а Number ос обы х в опрос ов не в ы з ы в ает : д л я реал из ации наш ихприм еров д ос т аточнобуд ет в кл ючит ь в негов с е т е же в ос ем ь операторов , из м енив т ипы ихпарам ет ров и в оз в ращаем ы хз начений на Number. Н ал ичие в кл ас с е, пред с т ав л яющем кол ь цо, м етод ов д л я ос ущес т в л ения д ел ения в ы гл яд ит нес кол ь кос т ранны м . Од нако, как показ ы в ает рас с м от ренны й в ы ш е прим ер кл ас с а PAryFraction, час т ичны й характ ер э той операции в с егд а м ожет бы т ь в ы ражен при пом ощи ис кл ючений. Пробл ем ы в оз никают в с в яз и с реал из ацией м етод ов кл ас с а Number. С од ной с тороны , очев ид но, чтоникакихс од ержат ел ь ны хд ейс т в ий они в ы пол нят ь не м огут. С д ругой — с инт акс ис из в ес т ной нам к нас тоящем у м ом ент у час т и яз ы ка т ребует, чтобы в с е м ет од ы кл ас с а бы л и как-тоопред ел ены . Сам ы м прос т ы м в ы ход ом из э той с ит уации яв л яет с я ис пол ь з ов ание «з агл уш ек» , т. е. под програм м , т ел о которы х пред с т ав л яет с обой пус той бл ок begin end. Н ед ос т аток д анногореш ения з акл ючает с я в том , что програм м ис т, раз рабат ы в ающий произ в од ны й кл ас с , м ожет прос то з абы т ь переопред ел ит ь некоторы е из них, з ал ожив , т ем с ам ы м , в програм м у пот енциал ь ны е ош ибки. 51
Другой под ход с ос тоит в прим енении а бстра ктн ых ме тодов, в ообще не им еющих реал из ации. Конт рол ь з а переопред ел ением т аких м етод ов берет на с ебя ком пил ятор. Объяв л ение абс т ракт ного м етод а, в отл ичие от прос то в ирт уал ь ного, з ав ерш ает с я д в ум я д ирект ив ам и: virtual и abstract. Какой-л ибо реал из ации д анны й м етод не т ребует, од нако и непос ред с т в енны й его в ы з ов в о в рем я в ы пол нения програм м ы прив ед ет к ис кл ючит ел ь ной с ит уации. Т аким образ ом , произ в од ны й кл ас с д ол жен обяз ат ел ь но з ам ес т ит ь в с е реал ь ноис пол ь з уем ы е им абс т ракт ны е м етод ы . В то в рем я, как обы чны е в ирт уал ь ны е м етод ы пос т епенно уточняют с в ойс т в а кл ас с ов в процес с е конкрет из ации, абс т ракт ны е чл ены поз в ол яют пол нос т ь ю отл ожит ь реал из ацию некоторогос в ойс т в а д отогом ом ент а, когд а э т а реал из ация с т анет в оз м ожной и необход им ой. Поэ том у кл ас с , с од ержащий хотя бы од ин нез ам ещенны й абс т ракт ны й м етод , ес т ес т в енно т акже наз ы в ат ь а бстра ктн ым. Абс т ракт ны е кл ас с ы , пожал уй, наибол ее точно с оот в ет с т в уют понят ию абс т ракт ного т ипа д анны х, окотором ш л а речь в начал е в торой час т и. И т ак, опред ел им кл ас с Number с л ед ующим образ ом . П р им ер 3.6. М од ул ь , с од ержащий опред ел ение кл ас с а Number. unit UnitNumber; interface type Number = class // абстрактное число public function Plus (n: Number): Number; virtual; abstract; function Minus(n: Number): Number; virtual; abstract; function Dot (n: Number): Number; virtual; abstract; function Slash(n: Number): Number; virtual; abstract; procedure Add (n: Number); virtual; abstract; // procedure Subtract (n: Number); virtual; abstract; // procedure MultiplyBy(n: Number); virtual; abstract; // procedure DivideBy (n: Number); virtual; abstract; // end;
// // // // абстрактные методы
implementation end.
М од ул ь UnitAlgorithms нужно т еперь м од ифициров ат ь с ов с ем нез начит ел ь но, необход им ол иш ь з ам енит ь им я Rational на Number в опред ел ении т ипа Matrix: 52
Matrix = array[1..n,1..n] of Number;
и в объяв л ении л окал ь ной перем енной p функции MultiplyMatrix. Ч тобы с д ел ат ь э т и из м енения в оз м ожны м и, раз д ел инт ерфейс а д анногом од ул я д ол жен под кл ючат ь UnitNumber в м ес тоUnitRational. В рез ул ь т ат е в с ехис прав л ений м ы пол учаем , наконец, обобщенны е ал горит м ы , пригод ны е д л я в ы чис л ений над л юбы м кол ь цом : д ос т аточнол иш ь с оз д ат ь под ход ящегопотом ка кл ас с а Number. При э том раз работ ка нов ы хкл ас с ов не буд ет оказ ы в ат ь никакогов л ияния на м од ул ь UnitAlgorithms, егоне прид ет с я д аже переком пил иров ат ь . В з акл ючение пос м от рим , как с л ед ует из м енит ь кл ас с ы Rational и PAryFraction, чтобы они с т ал и произ в од ны м и от Number. Вос пол ь з ов ав ш ис ь с л учаем , объяв им в с е м етод ы э т ихкл ас с ов в ирт уал ь ны м и. И с кл ючение с тоит с д ел ат ь л иш ь д л я конс т рукторов , в ы з ы в аем ы хобы чно пут ем яв ного указ ания им ени т ипа, и д л я функции PAryFraction.isPrime , реал из ация которой в д ейс т в ит ел ь нос т и никак не з ав ис ит от с од ержащегоее кл ас с а. П р им ер 3.7. М од ифициров анны й раз д ел интерфейс а м од ул я UnitRational. unit UnitRational; interface uses UnitNumber; // подключаем модуль с определением класса Number type Rational = class(Number) // указываем Number в качестве предка private num: Integer; den: Integer; protected procedure Normalize; virtual; procedure Reduce; virtual; public constructor Create; constructor CreateAs(n, m: Integer); function Plus (n: Number): Number; override; // function Minus(n: Number): Number; override; // function Dot (n: Number): Number; override; // замещаем function Slash(n: Number): Number; override; // абстрактные procedure Add (n: Number); override; // методы procedure Subtract (n: Number); override; // procedure MultiplyBy(n: Number); override; // procedure DivideBy (n: Number); override; //
53
procedure procedure function function end;
SetNum(value:Integer); SetDen(value:Integer); GetNum: Integer; GetDen: Integer;
virtual; virtual; virtual; virtual;
П р им ер 3.8. М од ифициров анны й раз д ел инт ерфейс а м од ул я UnitPAryFraction. unit UnitPAryFraction; interface uses UnitRational; type PAryFraction = class(Rational) private base: Integer; protected function isPrime(value: Integer): boolean; function isPowerOfBase(value: Integer): boolean; public constructor Create; constructor CreateAs(n, m: Integer); function Plus (n: Number): Number; override; // function Minus(n: Number): Number; override; // function Dot (n: Number): Number; override; // function Slash(n: Number): Number; override; // procedure Add (n: Number); override; // procedure Subtract (n: Number); override; // procedure MultiplyBy(n: Number); override; // procedure DivideBy (n: Number); override; // procedure SetDen (value: Integer); override; procedure SetBase(value: Integer); virtual; function GetBase: Integer; virtual; end;
virtual;
типы параметров и возвращаемых значений этих методов изменились
Объяв л ения м етод ов , ос ущес т в л яющих арифм ет ичес кие операции, т еперь с оотв ет с т в уют опред ел ениям из кл ас с а Number. Дл я кл ас с а PAryFraction т акая м од ификация нес ущес т в енна: з д ес ь пров ерка реал ь ногот ипа парам ет ра в ы пол нял ас ь и ранее. Ч тоже кас ает с я кл ас с а Rational, то в его операторы прид ет с я в нес т и с оот в ет с т в ующие из м енения. Пос кол ь ку м ы уже обс ужд ал и под обны й код в § 3.3, рас с м от рим л иш ь од ин
54
П р им ер 3.9. Н ов ая реал из ация м етод а Plus кл ас с а Rational. function Rational.Plus(n: Number): Number; var res: Rational; begin if n is Rational then begin // если n - рациональное число, res:=Rational.Create; // операция выполнима res.num:=self.num*n.den+self.den*n.num; res.den:=self.den*n.den; res.Normalize; res.Reduce; result:=res; end // иначе возбуждается исключение else raise Exception.Create('Invalid operation'); end;
Н ов ая в ерс ия отл ичает с я от пред ы д ущей л иш ь т ем , чтов ы яс няет, принад л ежит л и объект n кл ас с у Rational и, ес л и нет, в оз бужд ает ис кл ючение. У п раж нения 3.1. Опред ел ит е кл ас с Complex д л я работ ы с ком пл екс ны м и чис л ам и, произ в од ны й от кл ас с а Number. Прим енит е рас с м от ренны е обобщенны е ал горит м ы к м ат рицам ком пл екс ны хчис ел . 3.2. М од ифицируйт е операт оры кл ас с ов Rational и Complex т аким образ ом , чтобы в с юд у, гд е э тов оз м ожно, они д опус кал и с ов м ес т ное ис пол ь з ов ание в арифм ет ичес кихв ы раженияхрационал ь ны хи ком пл екс ны хчис ел . 3.3. Попробуйт е с оз д ат ь обобщенны й ал горит м прив ед ения м ат рицы к с т упенчатом у в ид у. Опред ел ит е, какихм етод ов кл ас с а Number не хв ат ает д л я его реал из ации, и д оработ айт е с оот в ет с т в ующим образ ом кл ас с ы Rational, Complex и PAryFraction.
55
4. Р Е А Л И ЗА Ц И Я О Б ЪЕ К Т Н О Й М О Д Е Л И 4.1. У казатели на п одп ро граммы Пос кол ь ку появ л ение объект но-ориент иров анного програм м иров ания не прив ел о к рас ш ирению набора ком анд , в ы пол няем ы хпроцес с орам и, с т анов ит с я очев ид но, чтообобщенны е ал горит м ы м ожнос оз д ав ат ь и при пом ощи т рад иционны хяз ы ков . В э той час т и м ы пос т роим прим ер реал из ации функций с л ожения и ум ножения произ в ол ь ны хм ат риц, ис пол ь з ующий л иш ь с ред с т в а процед урного програм м иров ания, и поход у д ел а от м ет им преим ущес т в а, которы е д ает прим енение объект ного рас ш ирения. Прив од им ы й з д ес ь код пом огает т акже понят ь , как реал из ует с я объект ная м од ел ь в некоторы хс ов рем енны хяз ы ках. Дл я в ы пол нения д инам ичес кого с в яз ы в ания нам пот ребует с я м анипул иров ат ь ад рес ам и под програм м . Дел ает с я э топри пом ощи с пециал ь ны х указ ат ел ей, опис анию которы х и пос в ящен д анны й параграф. Ч ит ат ел и, з наком ы е с э т им м ат ериал ом , м огут с раз у перейт и к § 4.2. Как из в ес т но, л юбой эл ем ент д анны х в ком пь ют ерах фон-Н ейм аннов с когот ипа характ ериз ует с я т рем я в ел ичинам и: раз м ером обл ас т и пам ят и, которую он з аним ает, ад рес ом э той обл ас т и и с обс т в енноз начением . Объяв л ение перем енной x т ипа Integer т ракт ует с я ком пил ятором как необход им ос т ь з арез ерв иров ат ь обл ас т ь пам ят и раз м ером в од но м аш инное с л ов о (им енно с тол ь ко т ребует д л я хранения т ип Integer) и обоз начит ь ее ад рес через x. Пос л е э того л юбое обращение к перем енной x инт ерпрет ирует с я им как операция с ячейкой поад рес у x. Пос кол ь ку кажд ая под програм м а раз м ещает с я в той же физ ичес кой пам ят и, что и д анны е, д л я нее т акже опред ел ены указ анны е т ри в ел ичины , из которы хнас инт ерес ует тол ь коод на — ад рес . Как и в с л учае перем енны х, с им в ол ичес ким обоз начением э тогоад рес а с л ужит им я под програм м ы , д л я операций же с ним ис пол ь з уют с я указ ат ел и ос обого в ид а — процед урны е. Дл я начал а рас с м от рим прим ер функции, в ы чис л яющей опред ел енны й инт еграл м етод ом т рапеций. function Integral(xmin, xmax: Real): Real; var i: Integer; dx: Real; begin dx:=(xmax-xmin)/100; result:=(f(xmin)+f(xmax))*dx/2;
56
for i:=1 to 99 do result:=result+f(xmin+dx*i)*dx; end;
Зд ес ь конкрет ны й в ид функции f не им еет никакогоз начения, д ос т аточнол иш ь пот ребов ат ь , чтобы она приним ал а од ин парам ет р и в оз в ращал а з начение т ипа Real. Ос ущес т в л яет с я э то пут ем объяв л ения f указ ат ел ем на функцию с оот в ет с т в ующего в ид а. С его пом ощь ю ал горит м у м ожно буд ет перед ав ат ь ад рес а раз л ичны х под програм м , с оз д ав аем ы хпол ь з ов ат ел ем пом ере необход им ос т и. Объяв л ение процед урногоуказ ат ел я с в од ит с я к опред ел ению с оот в ет с т в ующегопроцед урногот ипа. Пос л ед нее в ы гл яд ит как з агол ов ок под програм м ы , отл ичаяс ь от неготол ь коот с ут с т в ием конкрет ногоим ени. В наш ем с л учае т ип указ ат ел я f нужноопред ел ит ь т ак: type Integrand = function(x: Real): Real; // тип "Интегранд"
Т еперь с л ед ует л ибо объяв ит ь гл обал ь ную перем енную f нов ого т ипа: f: Integrand;
л ибо, чтобол ее пред почт ит ел ь но, д обав ит ь в з агол ов ок функции Integral еще од ин парам ет р: function Integral(f: Integrand; xmin, xmax: Real): Real;
В перв ом с л учае, кс т ат и, м ожно бы л о обойт ис ь и без яв ного з ад ания им ени т ипа, в кл ючив егоопред ел ение прям ов объяв л ение перем енной f: f: function(x: Real): Real;
Пред пол ожим т еперь , что в наш ем рас поряжении им еют с я т ри функции: function Constant(x: Real): Real; // константа begin result:=1; end; function Polynomial(x: Real): Real; // многочлен begin result:=x*x-x+2; end; function Trigonometric(x: Real): Real; // тригонометрическая функция begin result:=sin(x)+cos(x); end;
Тогд а с л ед ующий код f:=Constant; WriteLn(Integral(f,0,1)); f:=Polynomial; WriteLn(Integral(f,0,1)); f:=Trigonometric; WriteLn(Integral(f,0,1));
57
пос л ед ов ат ел ь но с опос т ав л яет перем енной f им ена (= ад рес а) в с ехт рех функций и в ы в од ит на э кран з начения инт еграл ов от них, в ы чис л енны х в од нихи т ехже пред ел ах: от 0 д о1. Зам ет им , что парам ет р в рас с м от ренны хфункцияхм ожно бы л о наз в ат ь им енем , отл ичны м от x, од нако его т ип и т ип в оз в ращаем ого з начения од ноз начноопред ел яют с я т ипом перем енной f. Е с л и з д ес ь буд ет обнаруженонес оот в ет с т в ие, ком пил ятор не д опус т ит прис в аив ания. Сл ед ует от м ет ит ь т акже, чт о в с т роенны е под програм м ы , наприм ер, sin, не м огут ис пол ь з ов ат ь с я д л я инициал из ации указ ат ел ей. При необход им ос т и ихм ожнопогруз ит ь в нут рь с обс т в енной процед уры ил и функции: function MySin(x: Real): Real; begin result:=sin(x); end;
Н ел ь з я прис в оит ь указ ат ел ю и ад рес л окал ь ной под програм м ы . И нт ерпрет ация ком пил ятором операции, которую с л ед ует в ы пол нит ь над процед урны м указ ат ел ем , з ав ис ит от пол ожения, з аним аем ого пос л ед ним в в ы ражении. Е с л и f и g — д в а указ ат ел я од ногот ипа и prog — под ход ящая д л я ихинициал из ации под програм м а, то: 1) в ы ражения f:=prog и g:=prog прив од ят, как м ы уже в ид ел и, к прис в аив анию ад рес а под програм м ы перем енны м ; 2) в ы ражение f:=g копирует з начение g в f; 3) л юбое д ругое ис пол ь з ов ание перем енны х f и g в ос приним ает с я как в ы з ов ад рес уем ой им и под програм м ы и д ол жно с опров ожд ат ь с я перед ачей необход им огочис л а з начений в качес т в е парам ет ров . Важно пом нит ь , что указ ат ел ь нел ь з я прим енят ь д л я в ы з ов а, не инициал из иров ав его з начение коррект ны м ад рес ом . Н аруш ение э того прав ил а почт и нав ерное прив од ит л ибок в оз никнов ению ис кл ючит ел ь ной с ит уации, л ибопрос ток з ав ис анию програм м ы . Показ ат ь , чтоперем енная не опред ел ена, м ожно, прис в оив ей з начение nil (егоже пол учают с т ат ичес кие указ ат ел и при с т арт е прил ожения). Дл я пров ерки коррект нос т и с од ержим ого ис пол ь з ует с я функция Assigned(const P) (з ад ан). Она в оз в ращает «ис т ину» , ес л и з начение перед анного указ ат ел я не nil, и «л ожь » в прот ив ном с л учае. Т аким образ ом , реал из ацию прив ед енной в ы ш е функции Integral им еет с м ы с л м од ифициров ат ь с л ед ующим образ ом . function Integral(f: Integrand; xmin, xmax: Real): Real; var i: Integer; dx: Real; begin if Assigned(f) then begin // если f<>nil, выполнить вычисления dx:=(xmax-xmin)/100;
58
result:=(f(xmin)+f(xmax))*dx/2; for i:=1 to 99 do result:=result+f(xmin+dx*i)*dx; end else raise Exception.Create('Integrand is undefined.'); // иначе возбудить исключение с сообщением // "Подынтегральная функция не определена." end;
Как и абс т ракт ны е м етод ы , процед урны е указ ат ел и поз в ол яют отл ожит ь на в рем я час т ь реал из ации ал горит м а. Б ол ее того, с ихпом ощь ю м ожноуправ л ят ь ход ом в ы пол нения програм м ы , в ы бирая д л я ис пол ь з ов ания т у под програм м у, кот орая яв л яет с я наибол ее под ход ящей в д анны й м ом ент. И м енноэ т и в оз м ожнос т и пот ребуют с я нам д л я реал из ации пол им орфногопов ед ения объектов в с л ед ующихприм ерах. 4.2. А б страктны е классы и о б о б щ енны е алго ритмы В пред ы д ущей час т и м ы в в ел и кл ас с Number д л я пред с т ав л ения эл ем ентов произ в ол ь ногокол ь ца. Зд ес ь т у же рол ь буд ут играт ь з апис и с л ед ующегов ид а: Number = packed record TypeNameIs: function(TypeName: String): Boolean; Destroy: procedure(var n: PNumber); Plus: function(n1, n2: PNumber): PNumber; Minus: function(n1, n2: PNumber): PNumber; Dot: function(n1, n2: PNumber): PNumber; Slash: function(n1, n2: PNumber): PNumber; Add: procedure(var n1: PNumber; n2: PNumber); Subtract: procedure(var n1: PNumber; n2: PNumber); MultiplyBy: procedure(var n1: PNumber; n2: PNumber); DivideBy: procedure(var n1: PNumber; n2: PNumber); end;
Кажд ая перем енная т ипа Number с од ержит д ес ят ь процед урны х указ ат ел ей, в ос ем ь из которы х пред наз начены д л я хранения ад рес ов под програм м , ос ущес т в л яющих арифм ет ичес кие операции. Пос кол ь ку д л я парам ет ров и в оз в ращаем ы хз начений пос л ед нихпот ребует с я ос ущес т в л ят ь прив ед ение т ипа, з д ес ь указ ан т ип PNumber, опред ел яем ы й как с с ы л ка на з апис ь т ипа Number: type PNumber = ^Number;
59
В отл ичие от кл ас с а Number, унас л ед ов анногоот TObject, м ы опред ел яем нов ы й т ип «с нул я» и поэ том у д ол жны с ам ос т оят ел ь нореал из ов ат ь в с е необход им ы е нам операции. Ф ункция TypeNameIs ис пол ь з ует с я д л я в ы яс нения т ипа объект а в ов рем я в ы пол нения програм м ы , процед ура Destroy — д л я егоуд ал ения3. Обе э т и под програм м ы м ожноопред ел ит ь уже с ейчас : function TypeNameIs(TypeName: String): Boolean; begin if TypeName='Number' then result:=true else result:=false; end; procedure Destroy(var n: PNumber); begin Dispose(n); // процедура Dispose освобождает область памяти // размером в одну запись типа Number end;
Н ес м от ря на то, чтоназ ы в ают с я функция и процед ура т ак же, как и указ ат ел и в опред ел ении т ипа Number, прис в аив ат ь ихад рес а пол ям объектов прид ет с я в ручную. Вв ед ем д л я э того с пециал ь ную функцию Create, пом ес т ив ее в од ин м од ул ь с объяв л ениям и т ипов Number, PNumber и опред ел ениям и под програм м TypeNameIs и Destroy: function Create: PNumber; begin New(result); result.Destroy:=Destroy; result.TypeNameIs:=TypeNameIs; end;
Она играет з д ес ь рол ь конс т рукт ора: раз м ещает в пам ят и э кз ем пл яр объект а, с опос т ав л яет некоторы м егопол ям под програм м ы и в оз в ращает ад рес в качес т в е з начения. Зам ет им , чт ок пол ям з апис и, ад рес уем ой перем енной result, как и к пол ям нас т оящегообъект а, д опус кает с я обращение без операции раз ы м енов ы в ания: result.Destroy в м ес то result^.Destroy. 3
Зд ес ь и д ал ее в этой час т и м ы буд ем ис пол ь з ов ат ь т ерм ины объект, м етод и нас л ед ов ание прим енит ел ь но к з апис ям , под черкив ая т ем с ам ы м анал огию с объект ной м од ел ь ю. Кром е того, как и ранее, объектом буд ет наз ы в ат ь с я и с обс т в еннообл ас т ь пам ят и, с од ержащая экз ем пл яр з апис и, и перем енная, с с ы л ающаяс я на эт у обл ас т ь .
60
Реш ение ос т ав ит ь процед урны е указ ат ел и, с оот в ет с т в ующие абс т ракт ны м м етод ам , неинициал из иров анны м и в ы гл яд ит в пол не ес т ес т в енны м , од накокажд ы й т акой указ ат ел ь пред с т ав л яет с обой пот енциал ь ную угроз у. Е с л и пол ь з ов ат ел ь попы т ает с я в ы з в ат ь м етод , ос ущес т в л яющий арифм ет ичес кую операцию, д л я объект а т ипа Number, тод ал ь нейш ий ход в ы пол нения прил ожения с т анов ит с я с ов ерш еннонепред с каз уем ы м . Поэтом у прав ил ь нее бы л обы опред ел ит ь под програм м ы function AbstractBinaryOperator(n1, n2: PNumber): PNumber; begin // абстрактный бинарный оператор raise Exception.Create('Call of abstract method.'); // просто возбудить исключение с сообщением // "Вызов абстрактного метода." end; procedure AbstractAssignOperator(var n1: PNumber; n2: PNumber); begin // абстрактный оператор с присвоением raise Exception.Create('Call of abstract method.'); end;
и в функции Create прис в аив ат ь ихад рес а в с ем с в обод ны м указ ат ел ям : result.Plus:=AbstractBinaryOperator; result.Minus:=AbstractBinaryOperator; ... result.Add:=AbstractAssignOperator; ...
Сл ед ует от м ет ит ь , что ком пил ятор в с егд а в ы д ает пред упрежд ение, ес л и в с т речает в ы з ов конс т руктора д л я кл ас с а с нез ам ещенны м и абс т ракт ны м и м етод ам и. При от каз е от ис пол ь з ов ания объект ной м од ел и никакой пом ощи с егос тороны ожид ат ь уже не приход ит с я.
Пос м от рим т еперь , как буд ут в ы гл яд ет ь наш и обобщенны е ал горит м ы . Пример 4.1. Процед урная в ерс ия м од ул я UnitAlgorithms. unit UnitAlgorithms; interface uses UnitNumber; // подключаем модуль с определениями типов // Number, PNumber и функции Create const n = 3;
61
type
Matrix = array[1..n,1..n] of PNumber; // элементы матрицы // ссылки на записи function AddMatrix(m1, m2: Matrix): Matrix; function MultiplyMatrix(m1, m2: Matrix): Matrix; implementation function AddMatrix(m1, m2: Matrix): Matrix; var i,j: Integer; begin for i:=1 to n do for j:=1 to n do result[i,j]:=m1[i,j].Plus(m1[i,j], m2[i,j]); { Вызывается подпрограмма, адресуемая полем Plus записи m1[i,j]. Ей в качестве параметров передаются объекты m1[i,j] и m2[i,j]. } end; function MultiplyMatrix(m1, m2: Matrix): Matrix; var i,j,k: Integer; p: PNumber; begin for i:=1 to n do for j:=1 to n do begin result[i,j]:=m1[i,1].Dot(m1[i,1],m2[1,j]); for k:=2 to n do begin p:=m1[i,k].Dot(m1[i,k],m2[k,j]); result[i,j].Add(result[i,j],p); p.Destroy(p); end; end; end; end.
Как э то ни уд ив ит ел ь но, код м од ул я почт и не из м енил с я. Сам ое с ущес т в енное отл ичие с ос т оит в том , чтоим я объект а, д л я которогов ы з ы в ает с я м етод , приход ит с я пов торят ь д в ажд ы . Перв ы й раз оноис пол ь з ует с я д л я опред ел ения ад рес а конкрет ной под програм м ы , в торой — как парам ет р э той под програм м ы . Вс пом ним , чтов объект ной м од ел и перв ы й парам ет р перед ает с я м етод у ав том ат ичес ки под им енем self. 4.3. Н аследование В прим ере 4.1 обращает на с ебя в ним ание то, как час то в ис ход ном т екс т е фигурируют указ ат ел и. И эл ем ент ы м ат риц, и пол я объектов с од ержат з начения, которы е м огут бы т ь ад рес ам и чегоугод но. Н а с ам ом 62
д ел е м ы ни в коем с л учае не д ол жны прим енят ь д анны е ал горит м ы к м ат рицам , эл ем ент ы которы х д ейс т в ит ел ь но им еют т ип Number, пос кол ь ку д л я з апис ей т акогот ипа указ ат ел и Plus, Add и Dot не опред ел ены . Вм ес то э того с л ед ует ис пол ь з ов ат ь с т рукт уры , с од ержащие ад рес а конкрет ны хпод програм м . Н од л я того, чтобы д анны е ад рес а обрабат ы в ал ис ь коррект но, они д ол жны наход ит с я на т ехже м ес т ах, чтои в з апис яхт ипа Number. Б ол ее точноэ тооз начает, чтокажд ая з ам ещающая с т рукт ура обяз ана в кл ючат ь в с е пол я т ипа Number, и рас пол агат ь с я они д ол жны в той же пос л ед ов ат ел ь нос т и и с т ем и же с м ещениям и от нос ит ел ь ноначал а объект а. С учетом с каз анного в ы ш е опред ел им т ип д л я хранения рационал ь ны хчис ел : Rational = packed record TypeNameIs: function(TypeName: String): Boolean; Destroy: procedure(var n: PNumber); Plus: function(n1: PNumber; n2: PNumber): PNumber; Minus: function(n1: PNumber; n2: PNumber): PNumber; Dot: function(n1: PNumber; n2: PNumber): PNumber; Slash: function(n1: PNumber; n2: PNumber): PNumber; Add: procedure(var n1: PNumber; n2: PNumber); Subtract: procedure(var n1: PNumber; n2: PNumber); MultiplyBy: procedure(var n1: PNumber; n2: PNumber); DivideBy: procedure(var n1: PNumber; n2: PNumber); Normalize: procedure(var r: PRational); Reduce: procedure(var r: PRational); num: Integer; den: Integer; end;
Е го начал о пол нос т ь ю пов т оряет с т рукт уру т ипа Number. К унас л ед ов анны м пол ям д обав л яет с я д в а указ ат ел я на процед уры норм ал из ации и с окращения и д в а обы чны хпол я д л я чис л ит ел я и з нам енат ел я. В объяв л енияхнов ы хпол ей фигурирует т ип PRational, опред ел яем ы й анал огичноPNumber: type PRational = ^Rational;
Дл я прив ед енной с т рукт уры м ожно реал из ов ат ь уже в с е операции, чтом ы и с д ел аем . Пример 4.2. Опред ел ение м етод ов т ипа Rational. procedure Normalize(var r: PRational); begin
63
if r.den<0 then begin r.num:=-r.num; r.den:=-r.den; end; end; function TypeNameIs(TypeName: String): Boolean; begin if (TypeName='Rational') or (TypeName='Number') then result:=true else result:=false; { Поскольку тип Rational является производным от Number, все его объекты принадлежат также и типу Number. Такое определение функции соответствует поведению оператора is. } end; procedure Destroy(var n: PNumber); var _self: PRational; begin _self:=PRational(n); // процедура Dispose определяет размер Dispose(_self); // освобождаемой области памяти по // формальному типу своего параметра end; procedure Add(var n1: PNumber; n2: PNumber); var _num,_den: Integer; _self: PRational; begin _self:=PRational(n1); // понижение типа; при использовании // объектной модели корректность обеспечивается компилятором if n2.TypeNameIs('Rational') then begin _num:=_self.num*PRational(n2).den+_self.den*PRational(n2).num; _den:=_self.den*PRational(n2).den; _self.num:=_num; _self.den:=_den; _self.Normalize(_self); _self.Reduce(_self); end else raise Exception.Create('Illegal operation'); end; function Plus(n1: PNumber; n2: PNumber): PNumber; var _self, res: PRational; begin _self:=PRational(n1); if n2.TypeNameIs('Rational') then begin res:=UnitRational.Create; res.num:=_self.num*PRational(n2).den+_self.den*PRational(n2).num; res.den:=_self.den*PRational(n2).den; res.Normalize(res); res.Reduce(res); result:=PNumber(res); // повышение типа; при использовании
64
// объектной модели допустимость проверяется во время компиляции end else raise Exception.Create('Illegal operation'); end; { Остальные арифметические операции и процедура Reduce реализуются аналогично. }
Н аибол ь ш ий инт ерес з д ес ь в ы з ы в ают функция Plus и процед ура Add, а т акже процед ура Destroy. Перв ы м д ел ом они ос ущес т в л яют без ус л ов ное прив ед ение т ипа парам ет ра n1. Э т а операция без опас на, ес л и д л я в ы з ов а м етод а и в качес т в е егоперв огопарам ет ра в с егд а ис пол ь з ует с я од ин и тот же объект. В с ам ом д ел е, ес л и ис пол нение бы л о перед ано под програм м е, от нос ящейс я к т ипу Rational, з начит ее ад рес с од ержал опол е некоторого объект а. При коррект но с проект иров анной иерархии нас л ед ов ания от с юд а с л ед ует, чтод анны й объект им еет т ип Rational ил и произ в од ны й от него. Пред пол агая, чтоэ тот же объект бы л перед ан под програм м е в качес т в е перв огопарам ет ра, м ы и пол учаем т ребуем ы й т ип д л я n1. В прив ед енны храс с ужд енияхкоррект нос т ь упом инает с я д в ажд ы : прим енит ел ь но к иерархии нас л ед ов ания и в ы з ов у под програм м . И в том , и в д ругом с л учае ее обес печение пол нос т ь ю л ожит с я на пл ечи раз работ чика. При ис пол ь з ов ании объект ной м од ел и э т ихпробл ем прос то не в оз никает. Дал ее д л я объект а n2 в ы пол няет с я м етод TypeNameIs, пров еряющий егореал ь ны й т ип. Обс ужд ение в опрос а отом , почем у з д ес ь буд ет в ы з в ана нужная в ерс ия функции, отл ожим д ос л ед ующегопараграфа. Ос т ал ь ны е инс т рукции переш л и из объект ной в ерс ии т ипа почт и без из м енений. Л иш ь в конце функции Plus с нов а в ы пол няет с я яв ное прив ед ение т ипа, т еперь уже в с торону пов ы ш ения. Т о, чтоэ тоим енно пов ы ш ение, с л ед ует тол ь коиз реал из ации функции TypeNameIs, им ит ирующей с т анд арт ны й оператор is. С точки з рения ком пил ятора т ипы Number и Rational с ов ерш еннонез ав ис им ы , поэ том у он не с чит ает д анное прис в аив ание з ав ед ом обез опас ны м . Опред ел им , наконец, функции-конс т рукторы объектов т ипа Rational. Пример 4.3. Конс т рукторы д л я т ипа Rational. function Create: PRational; begin New(result); // экземпляр записи размещается в памяти
65
result.TypeNameIs:=TypeNameIs; // result.Destroy:=Destroy; // result.Plus:=Plus; // result.Minus:=Minus; // процедурным указателям result.Dot:=Dot; // присваиваются адреса result.Slash:=Slash; // конкретных подпрограмм result.Add:=Add; // result.Subtract:=Subtract; // result.MultiplyBy:=MultiplyBy; // result.DivideBy:=DivideBy; // result.Normalize:=Normalize; // result.Reduce:=Reduce; // result.num:=0; // числовые поля инициализируются result.den:=1; // корректными значениями end; function CreateAs(n, m: Integer): PRational; begin if m<>0 then begin // знаменатель должен быть положителен result:=UnitRational.Create; // вызов функции, определенной выше result.num:=n; result.den:=m; end else raise Exception.Create('Illegal value of denominator'); end;
Зд ес ь уже ад рес а под програм м с опос т ав л яют с я в с ем указ ат ел ям , т ак что нез ам ещенны хабс т ракт ны хм етод ов не ос т ает с я. Кром е э того, инициал из иров ат ь приход ит с я т акже и чис л ов ы е пол я. Как и ранее, д л я уд обс т в а д обав л яет с я еще од ин конс т руктор — CreateAs. 4.4. Д инамическо е связы вание Рас с м от рим с л ед ующий код . Пример 4.4. Процед урны й в ариант програм м ы , д ем онс т рирующей работ у обобщенны хал горит м ов . program TestAlgorithm; {$APPTYPE CONSOLE} uses UnitAlgorithms, // здесь находятся обобщенные алгоритмы UnitNumber, // необходим для выполнения повышения типа UnitRational; // необходим для создания реальных переменных
66
procedure WriteMatrix(m: Matrix); var i,j:Integer; begin for i:=1 to n do begin for j:=1 to n do begin Write(PRational(m[i,j]).num,'/', PRational(m[i,j]).den, ' '); end; WriteLn; end; WriteLn; end; var m1,m2,m3,m4: Matrix; i,j: Integer; begin for i:=1 to n do for j:=1 to n do begin m1[i,j]:=PNumber(UnitRational.CreateAs(1,(2 shl i))); m2[i,j]:=PNumber(UnitRational.CreateAs(1,(2 shl j))); end; { Объекты типа Rational размещаются в памяти и инициализируются. Перед присваиванием ссылки на них следует привести к типу PNumber. } m3:=AddMatrix(m1,m2); // вызываются обобщенные функции m4:=MultiplyMatrix(m1,m2); // сложения и умножения WriteMatrix(m1); // WriteMatrix(m2); // содержимое матриц WriteMatrix(m3); // выводится на экран WriteMatrix(m4); // for i:=1 to n do for j:=1 to n do begin m1[i,j].Destroy(m1[i,j]); // m2[i,j].Destroy(m2[i,j]); // элементы всех матриц m3[i,j].Destroy(m3[i,j]); // удаляются из памяти m4[i,j].Destroy(m4[i,j]); // end; ReadLn; end.
Н ачав работ ат ь , прив ед енная програм м а перв ы м д ел ом с оз д ает объект ы т ипа Rational и ис пол ь з ует с с ы л ки на нихд л я инициал из ации эл ем ентов м ат риц. Пос кол ь ку т ипы Rational и Number с в яз аны д руг с д ругом л иш ь с ем ант ичес ки, з д ес ь опят ь т ребует с я ос ущес т в ит ь яв ное прив ед ение т ипа. Дал ее к пос т роенны м с т рукт урам д анны хприм еняет с я функция 67
AddMatrix, в ы з ы в ающая д л я кажд ого эл ем ент а перв ой м ат рицы (форм ал ь ноим еющегот ип PNumber) м етод Plus: result[i,j]:=m1[i,j].Plus(m1[i,j], m2[i,j]);
Ком пил ятор т ранс л ирует э тот в ы з ов с л ед ующим образ ом . Пол е Plus с тоит т рет ь им в опис ании т ипа Number, с л ед ов ат ел ь но, его с м ещение от начал а объект а опред ел яет с я раз м ером перв ы х д в ух. Учит ы в ая, чтоз апис ь бы л а объяв л ена как упаков анная (packed), т. е. ее пол я с л ед уют д руг з а д ругом без пропус ков , м ы пол учаем в ел ичину в д в а м аш инны хс л ов а (8 байт в 32-раз ряд ны хс ис т ем ах). Т аким образ ом , из обл ас т и пам ят и, ад рес уем ой эл ем ент ом m1[i, j], с чит ы в ает с я т рет ь е пос чет у м аш инное с л ов о и егоз начение инт ерпрет ирует с я как ад рес ис ком ой под програм м ы . Какой им енно т ип им еет объект m1[i, j] з д ес ь уже нев ажно, гл ав ное, чтобы он с од ержал ад рес коррект ной в ерс ии функции Plus в указ анном м ес т е. Вс пом ним т еперь , что, опред ел яя т ип Rational, м ы пол нос т ь ю пов торил и с т рукт уру з апис и Number, д обав ив нов ы е пол я в конец. Кром е того, при с оз д ании нов огообъект а д анногот ипа функции-конс т рукторы прис в аив ают в с ем егопол ям реал ь ны е ад рес а под програм м . Сл ед ов ат ел ь но, в наш ем конкрет ном с л учае с форм ул иров анное ус л ов ие с прав ед л ив ои в ы з ов ы м етод ов прив од ят к ис пол нению жел аем огокод а (с м . рис . 4.1). Т еперь с т анов ит с я понят но, чтопод раз ум ев ал ос ь под коррект но с проект иров анной иерархией нас л ед ов ания. Кажд ы й т ип-нас л ед ник д ол жен опред ел ять с я как упаков анная з апис ь (packed record) и аккурат но пов торять объяв л ения в с ехпол ей с в оегот ипа-пред ка. Е с л и м етод не яв л яет с я абс т ракт ны м , то в опред ел ения функций-конс т рукторов с л ед ует в кл ючать инициал из ацию с оот в ет с т в ующего ем у процед урного указ ат ел я. При этом с ов с ем не обяз ател ь но д л я кажд ого т акого м етод а з анов о с оз д ав ать реал из ующую под програм м у. Е с л и в ерс ия из т ипа-пред ка в с е еще под ход ит, в пол не м ожноис пол ь з ов ать ее ад рес пов торно. Перечис л енны е д ейс т в ия гарант ируют норм ал ь ную работ у прил ожения при ус л ов ии, чтов с е в ы пол няем ы е програм м ис том прив ед ения т ипа яв л яют с я д опус т им ы м и. Зд ес ь кл ючев ую рол ь играет функция пров ерки т ипа (в наш ем с л учае — TypeNameIs), анал огичная оператору is. В рас с м от ренном прим ере м ы неод нократ нопол ь з ов ал ис ь без ус л ов ны м прив ед ением , пос кол ь ку им еннот аков ы м онояв л яет с я в объект ной в ерс ии програм м ы из пред ы д ущей час т и. Пов с ем ес т ное д обав л ение пров ерок д ел ает работ у прил ожения бол ее пред с каз уем ой, нои з ам ед л яет его в ы пол нение. 68
+1 +2 +3
Ф ункция TypeNameIs д л я т ипа Rational
+4 +5 +6
Ф ункция Destroy д л я т ипа Rational
+7 +8 +9
Ф ункция Plus д л я т ипа Rational
+10 +11 +12
TypeNameIs Destroy Plus Minus Dot Slash Add Subtract MultiplyBy DivideBy Normalize Reduce
Реал ь ны й ад рес уем ы й объект в ов рем я в ы пол нения програм м ы
0
Ад рес уем ы й объект с точки з рения ком пил ятора
Сс ы л ка на объект т ипа Number
num
+13
den
+14
Рис . 4.1. Т аким образ ом , пом им опод д ержки абс т ракт ны хт ипов д анны х, о которы хм ы м ногогов орил и в час т и 2, объект ное рас ш ирение яз ы ка Pascal берет на с ебя час т ь рут инны хопераций, с в яз анны хс опред ел ением нов огот ипа, и, чтос ам ое гл ав ное, поз в ол яет с оз д ав ат ь обобщенны е ал горит м ы , не от каз ы в аяс ь от преим ущес т в с т рогой т ипиз ации. При э том з начит ел ь ная час т ь пров ерок и прив ед ений т ипа по-прежнем у произ в од ит с я не в ов рем я в ы пол нения, а на э т апе ком пил яции, чем д ос т игает с я бол ее бы с т рая работ а прил ожения. 4.5. В о п ро сы о п тимизац ии И с пол ь з ов анная нам и реал из ация нас л ед ов ания обес печив ает очень в ы с окую с корос т ь д инам ичес кого с в яз ы в ания. Дл я опред ел ения нужной под програм м ы т ребует с я из в л ечь из пам ят и в с его од ин указ ат ел ь , причем егоад рес пол учает с я из ад рес а объект а д обав л ением с м ещения, в ел ичина которогоиз в ес т на уже на э т апе ком пил яции. Как э точас тобы в ает, с тол ь бы с т ры й ал горит м прив од ит к з начит ел ь ном у перерас ход у пам ят и, в ы з в анном у хранением в кажд ом объект е 69
ад рес ов в с ехв ирт уал ь ны хм етод ов . Сэ коном ит ь м ожно, в ы д ел ив процед урны е указ ат ел и в от д ел ь ную, с ущес т в ующую в ед инс т в енном э кз ем пл яре с т рукт уру, которую обы чно наз ы в ают та бл ице й виртуа л ьн ых ме тодов (VMT — virtual method table). При т аком реш ении объект пом им о с обс т в енны хпол ей д ол жен с од ержат ь л иш ь од ин указ ат ел ь на с оот в ет с т в ующую егот ипу VMT. Вот как в э том с л учае буд ет в ы гл яд ет ь м од ул ь UnitNumber. Пример 4.5. Вторая реал из ация т ипа Number. unit UnitNumber; interface type PNumber = ^Number; PNumberVMT = ^NumberVMT; NumberVMT = packed record // таблица виртуальных методов типа Number TypeName: String; // имя типа Ancestor: PNumberVMT; // указатель на VMT типа-предка TypeNameIs: function(VMT: PNumberVMT; TypeName: String): Boolean; Create: function: PNumber; // конструктор Destroy: procedure(var n: PNumber); // деструктор Plus: function(n1, n2: PNumber): PNumber; // Minus: function(n1, n2: PNumber): PNumber; // Dot: function(n1, n2: PNumber): PNumber; // Slash: function(n1, n2: PNumber): PNumber; // операторы Add: procedure(var n1: PNumber; n2: PNumber); // Subtract: procedure(var n1: PNumber; n2: PNumber); // MultiplyBy: procedure(var n1: PNumber; n2: PNumber); // DivideBy: procedure(var n1: PNumber; n2: PNumber); // end; Number = packed record // тип Number VMT: PNumberVMT; end; var TypeNumber: NumberVMT; // единственный экземпляр VMT implementation function TypeNameIs(VMT: PNumberVMT; TypeName: String): Boolean; begin if TypeName=VMT.TypeName then result:=true
70
else if VMT.Ancestor<>nil then result:=VMT.Ancestor.TypeNameIs(VMT.Ancestor, TypeName) else result:=false; { Если значение параметра TypeName совпадает с именем, указанным в VMT, то результат - "истина". В противном случае он определяется версией той же функции из типа-предка, если таковой существует. } end; function Create: PNumber; begin New(result); result.VMT:=@TypeNumber; // инициализируем ссылку на VMT end; procedure Destroy(var n: PNumber); begin Dispose(n); end; initialization TypeNumber.TypeName:='Number'; TypeNumber.Ancestor:=nil; // у типа Number нет предка TypeNumber.TypeNameIs:=TypeNameIs; TypeNumber.Create:=Create; TypeNumber.Destroy:=Destroy; end.
Пос кол ь ку Number — абс т ракт ны й т ип, его объект не с од ержит ничего, кром е ад рес а т абл ицы в ирт уал ь ны хм етод ов , хранящейс я в от кры той перем енной TypeNumber. Запол нение ее пол ей проис ход ит од ин раз в процес с е инициал из ации м од ул я, т ак чтом ы пол учаем некоторы й в ы игры ш в ов рем ени при с оз д ании объектов . Зам ет им , что пом им о процед урны хуказ ат ел ей перем енная TypeNumber в кл ючает д в а пол я: TypeName (им я т ипа) и Ancestor (с с ы л ка на VMT т ипа-пред ка); онихи офункции TypeNameIs м ы погов орим ниже. Кром е того, функция-конс т руктор Create т еперь т акже яв л яет с я в иртуал ь ной. При ис пол ь з ов ании нов ой в ерс ии т ипа Number в ы з ов м етод ов ос ущес т в л яет с я пос ред с т в ом в ы ражений в ид а: n3:=n1.VMT.Plus(n1,n2); n1.VMT.Add(n1,n2);
71
Дл я переход а к нужной под програм м е процес с ор д ол жен из в л ечь из обл ас т и пам ят и, з аним аем ой объектом , указ ат ел ь на т абл ицу в ирт уал ь ны хм етод ов , д обав ит ь к нем у с м ещение, в ел ичина которого по-прежнем у опред ел яет с я на э т апе ком пил яции ис ход я из им ени м етод а, и с чит ат ь по пол ученном у ад рес у з начение процед урного указ ат ел я. Т аким образ ом , по с рав нению с пред ы д ущей реал из ацией м ы пол учаем од но д опол нит ел ь ное обращение к пам ят и, что не т ак уж м ал о, учит ы в ая час тот у ис пол ь з ов ания м ет од ов . Т ем не м енее, в ы игры ш от э коном ии пам ят и перев еш ив ает, и им енноэ т а с хем а прим еняет с я в Object Pascal (как, в прочем , и в некоторы х д ругих яз ы ках) д л я от ы с кания нужны х в ерс ий под програм м . Вы д ел ение в с ехпроцед урны хуказ ат ел ей в от д ел ь ную с т рукт уру прив ел ок появ л ению еще од ногос пос оба в ы з ов а м етод ов — пут ем прям огообращения к перем енной TypeNumber: TypeNumber.ИмяМетода(Параметры)
Яв ноуказ ы в ая т абл ицу в ирт уал ь ны хм етод ов , м ы од ноз начноопред ел яем ихв ерс ии и, т ем с ам ы м , пол нос т ь ю ис кл ючаем пол им орфиз м . Поэ том у под обны е в ы ражения раз ум но прим енят ь л иш ь д л я м етод ов , не з ав ис ящихот конкрет ного объект а: Create и TypeNameIs (в объект ной м од ел и т акие м етод ы наз ы в ают с я кл а ссовыми). Обрат им с я т еперь к реал из ации нас л ед ника — т ипа Rational. Пос кол ь ку он с од ержит и нов ы е пол я, и нов ы е м етод ы , нам прид ет с я д опол нит ь как опред ел ение объект а, т ак и с т рукт уру VMT. Код под програм м , пос рав нению с в ерс ией из § 4.3, из м енит с я очень нез начит ел ь но. Пример 4.6. Н ов ая реал из ация т ипа Rational. unit UnitRational; interface uses UnitNumber; // подключаем модуль с определением типа Number type PRational = ^Rational; PRationalVMT = ^RationalVMT; RationalVMT = TypeName: Ancestor: TypeNameIs: Create: Destroy:
packed record // VMT для типа Rational String; PNumberVMT; function(VMT: PNumberVMT; TypeName: String): Boolean; function: PNumber; procedure(var n: PNumber);
72
Plus: Minus: Dot: Slash: Add: Subtract: MultiplyBy: DivideBy: CreateAs: Normalize: Reduce: end;
function(n1: PNumber; n2: PNumber): PNumber; function(n1: PNumber; n2: PNumber): PNumber; function(n1: PNumber; n2: PNumber): PNumber; function(n1: PNumber; n2: PNumber): PNumber; procedure(var n1: PNumber; n2: PNumber); procedure(var n1: PNumber; n2: PNumber); procedure(var n1: PNumber; n2: PNumber); procedure(var n1: PNumber; n2: PNumber); function(n, m: Integer): PRational; // добавленные procedure(var r: PRational); // методы procedure(var r: PRational); //
Rational = packed record // тип Rational VMT: PRationalVMT; num: Integer; // добавленные den: Integer; // поля end; var TypeRational: RationalVMT; // единственный экземпляр VMT implementation uses SysUtils; // модуль включает поддержку исключений function Plus(n1: PNumber; n2: PNumber): PNumber; var _self, res: PRational; begin _self:=PRational(n1); if n2.VMT.TypeNameIs(n2.VMT, 'Rational') then begin res:=PRational(TypeRational.Create); res.num:=_self.num*PRational(n2).den+_self.den*PRational(n2).num; res.den:=_self.den*PRational(n2).den; res.VMT.Normalize(res); res.VMT.Reduce(res); result:=PNumber(res); // по сравнению с предыдущей версией изменился синтаксис вызова // функции TypeNameIs, конструктора, процедур Normalize и Reduce end else raise Exception.Create('Illegal operation'); end; ... // остальные операторы и процедуры Normalize и Reduce // модифицируются аналогично функции Plus function Create: PNumber; var res: PRational; begin New(res); res.VMT:=@TypeRational; // объекты ссылаются на VMT типа Rational
73
res.num:=0; res.den:=1; result:=PNumber(res); { Поскольку функция Create теперь является методом, ее заголовок определяется типом Number. В связи с этим здесь и в функциях CreateAs, Plus, Minus, Dot, Slash приходится делать взаимообратные приведения типа. } end; function CreateAs(n, m: Integer): PRational; begin if m<>0 then begin result:=PRational(Create); result.num:=n; result.den:=m; end else raise Exception.Create('Illegal value of denominator'); end; initialization TypeRational.TypeName:='Rational'; TypeRational.Ancestor:=@TypeNumber; // поле Ancestor ссылается на VMT типа Number TypeRational.TypeNameIs:=TypeNumber.TypeNameIs; // функция TypeNameIs остается той же, что и в типе Number TypeRational.Create:=Create; ... // остальные указатели инициализируются адресами // соответствующим образом названных подпрограмм end.
Т ак как обе з апис и, Rational и RationalVMT, пос т роены на ос нов е с т рукт ур т ипа Number, д инам ичес кое с в яз ы в ание з д ес ь буд ет работ ат ь факт ичес ки т ак же, как и рань ш е (рис . 4.2). Зам ет им д ал ее, чтопол я Ancestor (пред ок) объед иняют в с е т абл ицы в ирт уал ь ны хм етод ов , фигурирующие в проект е, в од ну д рев ов ид ную с т рукт уру (с м . рис . 4.3). Б л агод аря э том у, у нас появ л яет с я ед ины й м еханиз м д л я в ы з ов а с т ары хв ерс ий под програм м , анал огичны й оператору inherited.
74
Сс ы л ка на объект т ипа Number
VMT num
TypeName
den
Ancestor TypeNameIs Create
Ад рес уем ы й объект с точки з рения ком пил ятора
Destroy Plus
Реал ь ны й ад рес уем ы й объект в о в рем я в ы пол нения програм м ы
Minus Dot Slash Add Subtract MultiplyBy DivideBy
VMT ад рес уем огообъект а с точки з рения ком пил ятора
CreateAs Normalize
VMT реал ь ногоад рес уем ого объект а
Reduce
Рис . 4.2. ‘Number’
TypeName Ancestor TypeNameIs ...
TypeName
TypeName
TypeNameIs
Ancestor
...
Ancestor ‘Rational’
TypeNameIs ...
Рис . 4.3. 75
Другой нас л ед ник т ипа Number
С пом ощь ю д анного м еханиз м а м ы м ожем с т роит ь рекурс ив ны е ал горит м ы , под обны е функции TypeNameIs. В отл ичие от д ругихм етод ов , ее перв ы м парам ет ром яв л яет с я с с ы л ка не на конкрет ны й объект, а на с оот в ет с т в ующую ем у VMT (перем енны е т ипа PNumberVMT играют з д ес ь рол ь кл а ссовых ссыл ок). Э того в пол не д ос т аточно д л я опред ел ения им ени т ипа, хранящегос я в пол е TypeName. Е с л и онос ов пад ает с о с т рокой, перед анной в качес т в е в торогопарам ет ра, пров ерка з ав ерш ает с я. В прот ив ном с л учае, функция в ы з ы в ает с в ою же в ерс ию из т ипа, яв л яющегос я пред ком поот нош ению к т екущем у, и перед ает ей с с ы л ку на с оот в ет с т в ующую VMT. Т аким образ ом , м ы перем ещаем с я поиерархии нас л ед ов ания в направ л ении в ерш ины ; д обрав ш ис ь д о нее и не в с т рет ив ни од ногос ов пад ения, ал горит м в оз в ращает «л ожь » . В прив ед енном прим ере м ы ис пол ь з ов ал и од ну и т у же под програм м у д л я реал из ации м етод ов TypeNameIs обоих кл ас с ов . Вм ес т е с т ем , рекурс ив ны й в ы з ов произ в од ит с я в ней при пом ощи м еханиз м а д инам ичес когос в яз ы в ания, поэ том у им еет с я в оз м ожнос т ь прим енят ь опис анны й ал горит м , л иш ь начиная с некоторогоуров ня иерархии. Х отя рас с м от ренная в этом параграфе с хем а реал из ации д инам ичес когос в яз ы в ания поз в ол яет с ил ь нос э коном ит ь пам ят ь , при очень раз в итой иерархии нас л ед ов ания д аже этогом ожет оказ ат ь с я нед ос т аточно. Е с л и в баз ов ы хкл ас с ахобъяв л яет с я м ножес т в ов ирт уал ь ны хм етод ов , а з ам ещают с я они л иш ь из ред ка, то д ерев о VMT раз рас т ает с я д о в нуш ит ел ь ны храз м еров и при этом с од ержит яв ноиз бы точную инф орм ацию. Реш ением д анной пробл ем ы м ожет с л ужит ь д ругой ал горит м д инам ичес кого с в яз ы в ания, пос л ед ов ат ел ь но прос м ат рив ающий кл ас с ы , яв л яющиес я пред кам и т екущего, на пред м ет нал ичия в нихпод ход ящего м етод а. При этом от пад ает необход им ос т ь хранит ь ад рес а в с ехм етод ов в кажд ом кл ас с е, нои поис к т ребуем ой под програм м ы проис ход ит нес рав ним ом ед л еннее. В Object Pascal д анны й ал горит м ис пол ь з ует с я д л я в ы з ов а м етод ов , объяв л ение которы хз ав ерш ает с я д ирект ив ой dynamic.
76
З А К Л Ю ЧЕ Н И Е Как уже от м ечал ос ь в ов в ед ении, ос нов ное наз начение объект ноориент иров анной т ехнол огии — борь ба с ос л ожнос т ь ю програм м иров ания. Зд ес ь под с л ожнос т ь ю поним ает с я от нюд ь не в ит иев атос т ь ал горит м ов , а раз рас т ание раз м еров ис ход ны хт екс тов прил ожений в с очет ании с необход им ос т ь ю ихпос тоянной м од ификации. При ис пол ь з ов ании с т рукт урногопрограм м иров ания кажд ая с м ена в ерс ии т ребует от раз работ чика м ногочис л енны хрут инны хиз м енений и поним ания ед в а л и не в с егот екс т а проект а, напис анногок д анном у м ом ент у. Объект ны й под ход поз в ол яет з начит ел ь но ув ел ичит ь с т епень пов торяем ос т и код а, т. е. д ает в оз м ожнос т ь ис пол ь з ов ат ь уже напис анны е и отл аженны е библ иот еки без каких-л ибоиз м енений. Объект ная т ехнол огия в кл ючает т ри от нос ит ел ь нос ам ос тоят ел ь ны х м еханиз м а: под д ержку абс т ракт ны х т ипов д анны х, д инам ичес кое с в яз ы в ание и нас л ед ов ание. Во в торой час т и м ы м ного гов орил и о том , что абс т ракция д анны хнеобход им а д л я обес печения в з аим ной нез ав ис им ос т и раз работ чика т ипа и пол ь з ов ат ел я. Ст анд арт из ов анны й инт ерфейс в д анном с л учае с л ужит преград ой на пут и рас прос т ранения из м енений в ис ход ном т екс т е проект а. Динам ичес кое с в яз ы в ание в с очет ании с хранением информ ации о т ипе (RTTI — run-time type information) поз в ол яет в ы бират ь конкрет ную реал из ацию той ил и иной операции прям о в о в рем я в ы пол нения прил ожения, ис ход я из т ипа д анны х, к которы м она прим еняет с я. Э т а ид ея не яв л яет с я нов ой и д аже им еет под д ержку на аппарат ном уров не; как показ ы в ает § 4.1, с ред с т в ам и д л я ее реал из ации обл ад ают и процед урны е яз ы ки. Т рет ь я концепция — нас л ед ов ание — ус тоял ас ь з начит ел ь ном енее перв ы хд в ух. Она с л ужит д в ум ос нов ны м цел ям : обл егчению м од ификации т ипов д анны х и опред ел ению нов ы х прав ил с ов м ес т им ос т и при прис в аив ании. Понят но, чтов м ес то из м енения уже с ущес т в ующегот ипа гораз д опроще с оз д ат ь на егоос нов е нов ы й, д обав ив ил и переопред ел ив необход им ы е с в ойс т в а. Е с л и бы не появ л ение з ащищенного уров ня д ос т упа, э т а з ад ача с ам а пос ебе с тол ь же ус пеш ном огл а бы бы т ь реш ена и пос ред с т в ом ком поз иции, т. е. в кл ючения в нов ы й т ип с с ы л ки на объект с т арого. Од нако и тот, и д ругой с пос об не буд ут э ффект ив но работ ат ь при с охранении с т рогой т ипиз ации, д опус кающей прис в аив ания л иш ь 77
д л я объектов од ногои тогоже т ипа. Воз м ожной ал ьт ернат ив ой яв л яет с я пол ны й от каз от конт рол я т ипов в ов рем я раз работ ки, реал из ов анны й, наприм ер, в яз ы ке Smalltalk и некоторы хс ис т ем ахс им в ол ь ной м ат ем ат ики. У т акогопод ход а ес т ь д в а очев ид ны хм инус а: с ущес т в енное с нижение с корос т и работ ы прил ожения з а с чет пров ерки т ипа перед в ы пол нением кажд ой операции и отл оженное обнаружение ош ибок нес оот в ет с т в ия. Опис анное в нас тоящем пос обии опред ел ение с ов м ес т им ос т и на ос нов е иерархии нас л ед ов ания поз в ол яет произ в ес т и час т ь пров ерок т ипов л иш ь в о в рем я ком пил яции. Кром е того, с л ед с т в ием обяз ат ел ь ного в кл ючения в кл ас с -потом ок в с ехчл енов кл ас с а-пред ка яв л яют с я в ес ь м а э ффект ив ны е ал горит м ы д инам ичес когос в яз ы в ания, прив ед енны е в час т и 4. Вм ес т е с т ем д анная с хем а нас л ед ов ания не л иш ена и некоторы х нед ос т ат ков . Во-перв ы х, произ в од ны е кл ас с ы д ол жны , как прав ил о, д ос т аточно с ил ь но отл ичат ь с я от баз ов ы хпо уров ню абс т ракции. Рас с м от рим , наприм ер, м ножес т в а ком пл екс ны х, д ейс т в ит ел ь ны х, рационал ь ны х и цел ы хчис ел с с оот в ет с т в ующим и арифм ет ичес ким и операциям и. Вы раз ит ь им еющиес я в кл ючения на форм ал ь ном яз ы ке м ы м ожем , л иш ь с д ел ав кажд ую с л ед ующую с т рукт уру нас л ед ником пред ы д ущей. При э том , од нако, д л я пред с т ав л ения цел огочис л а нам прид ет с я ис пол ь з ов ат ь как м иним ум пару в ещес т в енны хи пару цел очис л енны хпол ей, д ос т ав ш ихс я от кл ас с ов ком пл екс ны хи рационал ь ны хчис ел . И с кл ючит ь л иш ние пол я м ожно, тол ь коунас л ед ов ав в с е кл ас с ы от од ногопред ка (под обного рас с м от ренном у в т рет ь ей час т и кл ас с у Number) и пот еряв , т ем с ам ы м , в з аим ос в яз и м ежд у ним и. Вторая неприят нос т ь в ы з в ана т ем , что от нош ения с ущнос т ей пред м ет ной обл ас т и д ал еконе в с егд а м огут бы т ь в ы ражены с пом ощь ю ориент иров анногод ерев а. Пред пол ожим , чтоу нас им еют с я кл ас с ы д л я пред с т ав л ения абс т ракт ногопол я и л инейноупоряд оченногом ножес т в а. Тогд а кл ас с рационал ь ны хчис ел , очев ид но, д ол жен бы т ь нас л ед ником ихобоих, чтов оз м ожно л иш ь при ис пол ь з ов ании той ил и иной форм ы м ножес т в енногонас л ед ов ания. Од на из в оз никающихна э том пут и пробл ем — конфл икт им ен: баз ов ы е кл ас с ы м огут с од ержат ь чл ены с од инаков ы м и ид ент ификаторам и. Зд ес ь инт ерес но от м ет ит ь , что з ав ис им ос т и м ежд у м од ул ям и с некоторой натяжкой т акже м ожнорас с м ат рив ат ь как форм у м ножес т в енного нас л ед ов ания. Под робное опис ание пробл ем ы перекры в ающихс я объяв л ений в д анном конкрет ном с л учае с од ержит § 1.3.
78
Т аким образ ом , з ад ача поис ка ад екв ат ной и э ффект ив ной форм ал из ации пред м ет ной обл ас т и в яз ы кахпрограм м иров ания в с е еще д ал ека от с в оего реш ения, хот я объект ная т ехнол огия, без ус л ов но, с д ел ал а бол ь ш ой ш аг в э том направ л ении. В з акл ючение попы т аем с я от в ет ит ь на в опрос , почем у объект но-ориент иров анны е яз ы ки при в с ех с в оих преим ущес т в ахникак не м огут в ы т ес нит ь яз ы ки процед урны е. Причин э том у, как м иним ум , д в е. Во-перв ы х, хранение информ ации о т ипе объект а в о в рем я в ы пол нения неиз бежно ув ел ичив ает з аним аем ы й им объем пам ят и. Дл я кл ас с ов , яв л яющихся анал огам и с кал ярны хт ипов д анны х, э тоув ел ичение в пол не м ожет оказ ат ь с я д в ух-т рехкрат ны м . Во-в торы х, л юбой ал горит м д инам ичес кого с в яз ы в ания по с рав нению с ос в яз ы в анием с т ат ичес ким т ребует некоторы хд опол нит ел ь ны х м анипул яций с пам ят ь ю. Как уже от м ечал ос ь , раз в ит ие объект ной т ехнол огии не пов л иял она архит ект уру процес с оров , т ак чтоопт им из ация на аппарат ном уров не з д ес ь нев оз м ожна. В рез ул ьт ат е, ал горит м ы , крит ичны е ков рем ени в ы пол нения, по-прежнем у пиш ут с ис пол ь з ов анием т рад иционны хс ред с т в . Т ем не м енее, с рос том в оз м ожнос т ей ком пь ют еров кол ичес т в о прил ожений, д л я которы хуказ анны е пробл ем ы нес ущес т в енны , в с е в оз рас т ает. Соот в ет с т в енно, приобрет ает попул ярнос т ь и объект ны й под ход к ихраз работ ке. Что читать даль ш е? Н ес м от ря на обил ие книг оDelphi, бол ь ш инс т в оиз нихс од ержит в ес ь м а пов ерхнос т ное опис ание яз ы ка Object Pascal, напом инающее с корее с прав очник, нежел и учебник. От час т и бл агод аря э том у не т еряет попул ярнос т и л ит ерат ура, кас ающаяс я пред ы д ущегопрод укт а фирм ы Borland — яз ы ка Turbo Pascal. К с ожал ению, ис пол ь з ов ат ь ее д л я из учения объект но-ориент иров анногопрограм м иров ания в Delphi нел ь з я: им енно объект ная м од ел ь прет ерпел а наибол ь ш ие из м енения при переход е к нов ой с ред е раз работ ки. И з с каз анного в ы т екает нес кол ь ко неожид анная реком енд ация: д л я бол ее гл убокого з наком с т в а с объект ной т ехнол огией и ее реал из ацией в Delphi раз ум ноис пол ь з ов ат ь книги, опис ы в ающие програм м иров ание на яз ы кахC++ и Java. Зд ес ь в ы бор ис точников информ ации з начит ел ь нош ире; м ногоценны хс в ед ений оприм енении объект ногопод ход а м ожнопочерпнут ь , наприм ер, из с л ед ующихиз д аний:
79
Л иппма н С ., Л а ж ойе Ж . Яз ы к програм м иров ания C++. Вв од ны й курс , 3-е из д . // Спб. – М .: Н ев с кий д иал ект – ДМ К Прес с , 2003. 1104 с . Эккел ь Б. Ф ил ос офия Java. Б ибиот ека програм м ис т а, 2-е из д . // Спб.: Пит ер, 2001. 880 с . Как уже от м ечал ос ь , объект ны е м од ел и яз ы ков Pascal, C++ и Java им еют м ного общего, и ихс рав нит ел ь ны й анал из поз в ол яет л учш е понят ь пл юс ы и м инус ы конкрет ной реал из ации. Сов м ес т ное опис ание объект но-ориент иров анногопрограм м иров ания в с ред ахBorland Pascal, Borland C++, Borland Delphi и Borland C++ Builder с од ержит учебник Ива н ова Г. С ., Ничуш кин а Т. Н., П уг а че в Е . К . Объект но-ориент иров анное програм м иров ание: Учебник д л я в уз ов // М .: И з д -в о М Г Т У им . Н . Э . Б аум ана, 2001. 320 с . Пос л е оз наком л ения с реал из ацией объект ногопод ход а хотя бы в д в ухяз ы кахпрограм м иров ания им еет с м ы с л пол учит ь , наконец, общее пред с т ав л ение ос пос обахпод д ержки объект ной т ехнол огии. От прав ной точкой з д ес ь м ожет с л ужит ь книга С е бе ста Р. Ос нов ны е концепции яз ы ков програм м иров ания, 5-е из д . // М .: И з д . д ом «Вил ь ям с » , 2001. 672 с .
80