ukturnogo proektirovaniya; [2] Adoj ili metodami proektirovaniya s pomoshch'yu abstrakcii dannyh; [3] yazykami, blizkimi Smalltalk ili Lisp. V kazhdom sluchae sleduet reshit': nepravil'no vybran yazyk realizacii (schitaya, chto metod proektirovaniya vybran verno), ili razrabotchiku ne udalos' prisposobit'sya i ocenit' yazyk (schitaya, chto yazyk realizacii vybran verno). Sleduet skazat', chto net nichego neobychnogo ili pozornogo v takom rashozhdenii. Prosto eto rashozhdenie, kotoroe privedet k neoptimal'nomu proektu, vozlozhit dopolnitel'nuyu rabotu na programmistov, a v sluchae, kogda struktura ponyatij proekta znachitel'no bednee struktury yazyka S++, to i na samih razrabotchikov. Otmetim, chto neobyazatel'no vse programmy dolzhny strukturirovat'sya opirayas' na ponyatiya klassov i (ili) ierarhij klassov, i neobyazatel'no vsyakaya programma dolzhna ispol'zovat' vse sredstva, predostavlyaemye S++. Kak raz naoborot, dlya uspeha proekta neobhodimo, chtoby lyudyam ne navyazyvali ispol'zovanie yazykovyh sredstv, s kotorymi oni tol'ko poznakomilis'. Cel' posleduyushchego izlozheniya ne v tom, chtoby navyazat' dogmatichnoe ispol'zovanie klassov, ierarhij i strogo tipizirovannyh interfejsov, a v tom, chtoby pokazat' vozmozhnosti ih ispol'zovaniya vsyudu, gde pozvolyaet oblast' prilozheniya, ogranicheniya S++ i opyt ispolnitelej. V $$12.1.4 budut rassmotreny podhody k razlichnomu ispol'zovaniyu S++ v proekte pod zagolovkom "Proekt-gibrid". 12.1.1 Ignorirovanie klassov Rassmotrim pervyj iz ukazannyh momentov - ignorirovanie klassov. V takom sluchae poluchivshayasya programma na S++ budet priblizitel'no ekvivalentna S-programme, razrabotannoj po tomu zhe proektu, i, mozhno skazat', chto oni budut priblizitel'no ekvivalentny programmam na Ade ili Kobole, razrabotannym po nemu zhe. Po suti proekt sostavlen kak nezavisyashchij ot yazyka realizacii, chto prinuzhdaet programmista ogranichivat'sya obshchim podmnozhestvom yazykov S, Ada ili Kobol. Zdes' est' svoi preimushchestva. Naprimer, poluchivsheesya v rezul'tate strogoe razdelenie dannyh i programmnogo koda pozvolyaet legko ispol'zovat' tradicionnye bazy dannyh, kotorye razrabotany dlya takih programm. Poskol'ku ispol'zuetsya ogranichennyj yazyk programmirovaniya, ot programmistov trebuetsya men'she opytnosti (ili, po krajnej mere drugoj ee uroven'). Dlya mnogih prilozhenij, naprimer, dlya tradicionnyh baz dannyh, rabotayushchih s fajlom posledovatel'no, takoj podhod vpolne razumen, a tradicionnye priemy, otrabotannye za desyatiletiya, vpolne adekvatny zadache. Odnako tam, gde oblast' prilozheniya sushchestvenno otlichaetsya ot tradicionnoj posledovatel'noj obrabotki zapisej (ili simvolov), ili slozhnost' zadachi vyshe, kak, naprimer, v dialogovoj sisteme CASE, nedostatok yazykovoj podderzhki abstraktnyh dannyh iz-za otkaza ot klassov (esli ih ne uchityvat') povredit proektu. Slozhnost' zadachi ne umen'shitsya, no, poskol'ku sistema realizovana na obednennom yazyke, struktura programmy ploho budet otvechat' proektu. U nee slishkom bol'shoj ob容m, ne hvataet proverki tipov, i, voobshche, ona ploho prisposoblena dlya ispol'zovaniya razlichnyh vspomogatel'nyh sredstv. |to put', privodyashchij k koshmaram pri ee soprovozhdenii. Obychno dlya preodoleniya ukazannyh trudnostej sozdayut special'nye sredstva, podderzhivayushchie ponyatiya, ispol'zuemye v proekte. Blagodarya im sozdayutsya konstrukcii bolee vysokogo urovnya i organizuyutsya proverki s cel'yu kompensirovat' defekty (ili soznatel'noe obednenie) yazyka realizacii. Tak metod proektirovaniya stanovitsya samocel'yu, i dlya nego sozdaetsya special'nyj yazyk programmirovaniya. Takie yazyki programmirovaniya v bol'shinstve sluchaev yavlyayutsya plohoj zamenoj shiroko rasprostranennyh yazykov programmirovaniya obshchego naznacheniya, kotorye soprovozhdayutsya podhodyashchimi sredstvami proektirovaniya. Ispol'zovat' S++ s takim ogranicheniem, kotoroe dolzhno kompensirovat'sya pri proektirovanii special'nymi sredstvami, bessmyslenno. Hotya nesootvetstvie mezhdu yazykom programmirovaniya i sredstvami proektirovaniya mozhet byt' prosto stadiej processa perehoda, a znachit vremennym yavleniem. Samoj tipichnoj prichinoj ignorirovaniya klassov pri proektirovanii yavlyaetsya prostaya inerciya. Tradicionnye yazyki programmirovaniya ne predostavlyayut ponyatiya klassa, i v tradicionnyh metodah proektirovaniya otrazhayutsya etot nedostatok. Obychno v processe proektirovaniya naibol'shee vnimanie udelyaetsya razbieniyu zadachi na procedury, proizvodyashchie trebuemye dejstviya. V glave 1 eto ponyatie nazyvalos' procedurnym programmirovaniem, a v oblasti proektirovaniya ono imenuetsya kak funkcional'naya dekompoziciya. Voznikaet tipichnyj vopros "Mozhno li ispol'zovat' S++ sovmestno s metodom proektirovaniya, baziruyushchimsya na funkcional'noj dekompozicii?" Da, mozhno, no, veroyatnee vsego, v rezul'tate vy pridete k ispol'zovaniyu S++ kak prosto uluchshennogo S so vsemi ukazannymi vyshe problemami. |to mozhet byt' priemlemo na period perehoda na novyj yazyk, ili dlya uzhe zavershennogo proektirovaniya, ili dlya podzadach, v kotoryh ispol'zovanie klassov ne daet sushchestvennyh vygod (esli uchityvat' opyt programmirovaniya na S++ k dannomu momentu), no v obshchem sluchae na bol'shom otrezke vremeni otkaz ot svobodnogo ispol'zovaniya klassov, svyazannyj s metodom funkcional'noj dekompozicii, nikak ne sovmestim s effektivnym ispol'zovaniem S++. Procedurno-orientirovannyj i ob容ktno-orientirovannyj podhody k programmirovaniyu razlichayutsya po svoej suti i obychno vedut k sovershenno raznym resheniyam odnoj zadachi. |tot vyvod veren kak dlya stadii realizacii, tak i dlya stadii proektirovaniya: vy koncentriruete vnimanie ili na predprinimaemyh dejstviyah, ili na predstavlyaemyh sushchnostyah, no ne na tom i drugom odnovremenno. Togda pochemu metod ob容ktno-orientirovannogo proektirovaniya predpochtitel'nee metoda funkcional'noj dekompozicii? Glavnaya prichina v tom, chto funkcional'naya dekompoziciya ne daet dostatochnoj abstrakcii dannyh. A otsyuda uzhe sleduet, chto proekt budet - menee podatlivym k izmeneniyam, - menee prisposoblennym dlya ispol'zovaniya razlichnyh vspomogatel'nyh sredstv, - menee prigodnym dlya parallel'nogo razvitiya i - menee prigodnym dlya parallel'nogo vypolneniya. Delo v tom, chto funkcional'naya dekompoziciya vynuzhdaet ob座avlyat' "vazhnye" dannye global'nymi, poskol'ku, esli sistema strukturirovana kak derevo funkcij, vsyakoe dannoe, dostupnoe dvum funkciyam, dolzhno byt' global'nym po otnosheniyu k nim. |to privodit k tomu, chto "vazhnye" dannye "vsplyvayut" k vershine dereva, po mere togo kak vse bol'shee chislo funkcij trebuet dostupa k nimX. X V tochnosti tak zhe proishodit v sluchae ierarhii klassov s odnim kornem, kogda "vazhnye" dannye vsplyvayut po napravleniyu k bazovomu klassu. Kogda my koncentriruem vnimanie na opisaniyah klassov, zaklyuchayushchih opredelennye dannye v obolochku, to zavisimosti mezhdu razlichnymi chastyami programmy vyrazheny yavno i mozhno ih prosledit'. Eshche bolee vazhno to, chto pri takom podhode umen'shaetsya chislo zavisimostej v sisteme za schet luchshej rasstanovki ssylok na dannye. Odnako, nekotorye zadachi luchshe reshayutsya s pomoshch'yu nabora procedur. Smysl "ob容ktno-orientirovannogo" proektirovaniya ne v tom, chtoby udalit' vse global'nye procedury iz programmy ili ne imet' v sisteme procedurno-orientirovannyh chastej. Osnovnaya ideya skoree v tom, chto klassy, a ne global'nye procedury stanovyatsya glavnym ob容ktom vnimaniya na stadii proektirovaniya. Ispol'zovanie procedurnogo stilya dolzhno byt' osoznannym resheniem, a ne resheniem, prinimaemym po umolchaniyu. Kak klassy, tak i procedury sleduet primenyat' soobrazno oblasti prilozheniya, a ne prosto kak neizmennye metody proektirovaniya. 12.1.2 Ignorirovanie nasledovaniya Rassmotrim variant 2 - proekt, kotoryj ignoriruet nasledovanie. V etom sluchae v okonchatel'noj programme prosto ne ispol'zuyutsya vozmozhnosti osnovnogo sredstva S++, hotya i poluchayutsya opredelennye vygody pri ispol'zovanii S++ po sravneniyu s ispol'zovaniem yazykov S, Paskal', Fortran, Kobol i t.p. Obychnye dovody v pol'zu etogo, pomimo inercii, utverzhdeniya, chto "nasledovanie - eto detal' realizacii", ili "nasledovanie prepyatstvuet upryatyvaniyu informacii", ili "nasledovanie zatrudnyaet vzaimodejstvie s drugimi sistemami programmirovaniya". Schitat' nasledovanie vsego lish' detal'yu realizacii - znachit ignorirovat' ierarhiyu klassov, kotoraya mozhet neposredstvenno modelirovat' otnosheniya mezhdu ponyatiyami v oblasti prilozheniya. Takie otnosheniya dolzhny byt' yavno vyrazheny v proekte, chtoby dat' vozmozhnost' razrabotchiku produmat' ih. Sil'nye dovody mozhno privesti v pol'zu isklyucheniya nasledovaniya iz teh chastej programmy na S++, kotorye neposredstvenno vzaimodejstvuyut s programmami, napisannymi na drugih yazykah. No eto ne yavlyaetsya dostatochnoj prichinoj, chtoby otkazat'sya ot nasledovaniya v sisteme v celom, eto prosto dovod v pol'zu togo, chtoby akkuratno opredelit' i inkapsulirovat' programmnyj interfejs s "vneshnim mirom". Analogichno, chtoby izbavit'sya ot bespokojstva, vyzvannogo putanicej s upryatyvaniem informacii pri nalichii nasledovaniya, nado ostorozhno ispol'zovat' virtual'nye funkcii i zakrytye chleny, no ne otkazyvat'sya ot nasledovaniya. Sushchestvuet dostatochno mnogo situacij, kogda ispol'zovanie nasledovaniya ne daet yavnyh vygod, no politika "nikakogo nasledovaniya" privedet k menee ponyatnoj i menee gibkoj sisteme, v kotoroj nasledovanie "poddelyvaetsya" s pomoshch'yu bolee tradicionnyh konstrukcij yazyka i proektirovaniya. Dlya bol'shih proektov eto sushchestvenno. Bolee togo, vpolne vozmozhno, chto nesmotrya na takuyu politiku, nasledovanie vse ravno budet ispol'zovat'sya, poskol'ku programmisty, rabotayushchie na S++, najdut ubeditel'nye dovody v pol'zu proektirovaniya s uchetom nasledovaniya v razlichnyh chastyah sistemy. Takim obrazom, politika "nikakogo nasledovaniya" privedet lish' k tomu, chto v sisteme budet otsutstvovat' celostnaya obshchaya struktura, a ispol'zovanie ierarhii klassov budet ogranicheno opredelennymi podsistemami. Inymi slovami, bud'te nepredubezhdennymi. Ierarhiya klassov ne yavlyaetsya obyazatel'noj chast'yu vsyakoj horoshej programmy, no est' massa situacij, kogda ona mozhet pomoch' kak v ponimanii oblasti prilozheniya, tak i v formulirovanii reshenij. Utverzhdenie, chto nasledovanie mozhet nepravil'no ili chrezmerno ispol'zovat'sya, sluzhit tol'ko dovodom v pol'zu ostorozhnosti, a vovse ne v pol'zu otkaza ot nego. 12.1.3 Ignorirovanie staticheskogo kontrolya tipov Rassmotrim variant 3, otnosyashchijsya k proektu, v kotorom ignoriruetsya staticheskij kontrol' tipov. Rasprostranennye dovody v pol'zu otkaza na stadii proektirovaniya ot staticheskogo kontrolya tipov svodyatsya k tomu, chto "tipy - eto produkt yazykov programmirovaniya", ili chto "bolee estestvenno rassuzhdat' ob ob容ktah, ne zabotyas' o tipah", ili "staticheskij kontrol' tipov vynuzhdaet nas dumat' o realizacii na slishkom rannem etape". Takoj podhod vpolne dopustim do teh por, poka on rabotaet i ne prinosit vreda. Vpolne razumno na stadii proektirovaniya ne zabotit'sya o detalyah proverki tipov, i chasto vpolne dopustimo na stadii analiza i nachal'nyh stadiyah proektirovaniya polnost'yu zabyt' o voprosah, svyazannyh s tipami. V to zhe vremya, klassy i ierarhii klassov ochen' polezny na stadii proektirovaniya, v chastnosti, oni dayut nam bol'shuyu opredelennost' ponyatij, pozvolyayut tochno zadat' vzaimootnosheniya mezhdu ponyatiyami i pomogayut rassuzhdat' o ponyatiyah. Po mere razvitiya proekta eta opredelennost' i tochnost' preobrazuetsya vo vse bolee konkretnye utverzhdeniya o klassah i ih interfejsah. Vazhno ponimat', chto tochno opredelennye i strogo tipizirovannye interfejsy yavlyayutsya fundamental'nym sredstvom proektirovaniya. YAzyk S++ byl sozdan kak raz s uchetom etogo. Strogo tipizirovannyj interfejs garantiruet, chto tol'ko sovmestimye chasti programmy mogut byt' skompilirovany i skomponovany voedino, i tem samym pozvolyaet delat' otnositel'no strogie dopushcheniya ob etih chastyah. |ti dopushcheniya obespechivayutsya sistemoj tipov yazyka. V rezul'tate svodyatsya k minimumu proverki na etape vypolneniya, chto povyshaet effektivnost' i privodit k znachitel'nomu sokrashcheniyu fazy integracii chastej proekta, realizovannyh raznymi programmistami. Real'nyj polozhitel'nyj opyt integracii sistemy so strogo tipizirovannymi interfejsami privel k tomu, chto voprosy integracii voobshche ne figuriruyut sredi osnovnyh tem etoj glavy. Rassmotrim sleduyushchuyu analogiyu: v fizicheskom mire my postoyanno soedinyaem razlichnye ustrojstva, i sushchestvuet kazhushcheesya beskonechnym chislo standartov na soedineniya. Glavnaya osobennost' etih soedinenij: oni special'no sproektirovany takim obrazom, chtoby sdelat' nevozmozhnym soedinenie dvuh ustrojstv, nerasschitannyh na nego, to est' soedinenie dolzhno byt' sdelano edinstvennym pravil'nym sposobom. Vy ne mozhete podsoedinit' elektrobritvu k rozetke s vysokim napryazheniem. Esli by vy smogli sdelat' eto, to sozhgli by britvu ili sgoreli sami. Massa izobretatel'nosti byla proyavlena, chtoby dobit'sya nevozmozhnosti soedineniya dvuh nesovmestimyh ustrojstv. Al'ternativoj odnovremennogo ispol'zovaniya neskol'kih nesovmestimyh ustrojstv mozhet posluzhit' takoe ustrojstvo, kotoroe samo sebya zashchishchaet ot nesovmestimyh s nim ustrojstv, podklyuchayushchihsya k ego vhodu. Horoshim primerom mozhet sluzhit' stabilizator napryazheniya. Poskol'ku ideal'nuyu sovmestimost' ustrojstv nel'zya garantirovat' tol'ko na "urovne soedineniya", inogda trebuetsya bolee dorogaya zashchita v elektricheskoj cepi, kotoraya pozvolyaet v dinamike prisposobit'sya ili (i) zashchitit'sya ot skachkov napryazheniya. Zdes' prakticheski pryamaya analogiya: staticheskij kontrol' tipov ekvivalenten sovmestimosti na urovne soedineniya, a dinamicheskie proverki sootvetstvuyut zashchite ili adaptacii v cepi. Rezul'tatom neudachnogo kontrolya kak v fizicheskom, tak i v programmnom mire budet ser'eznyj ushcherb. V bol'shih sistemah ispol'zuyutsya oba vida kontrolya. Na rannem etape proektirovaniya vpolne dostatochno prostogo utverzhdeniya: "|ti dva ustrojstva neobhodimo soedinit'"; no skoro stanovitsya sushchestvennym, kak imenno sleduet ih soedinit': "Kakie garantii daet soedinenie otnositel'no povedeniya ustrojstv?", ili "Vozniknovenie kakih oshibochnyh situacij vozmozhno?", ili "Kakova priblizitel'naya cena takogo soedineniya?" Primenenie "staticheskoj tipizacii" ne ogranichivaetsya programmnym mirom. V fizike i inzhenernyh naukah povsemestno rasprostraneny edinicy izmereniya (metry, kilogrammy, sekundy), chtoby izbezhat' smeshivaniya nesovmestimyh sushchnostej. V nashem opisanii shagov proektirovaniya v $$11.3.3 tipy poyavlyayutsya na scene uzhe na shage 2 (ochevidno, posle neskol'ko iskusstvennogo ih rassmotreniya na shage 1) i stanovyatsya glavnoj temoj shaga 4. Staticheski kontroliruemye interfejsy - eto osnovnoe sredstvo vzaimodejstviya programmnyh chastej sistemy na S++, sozdannyh raznymi gruppami, a opisanie interfejsov etih chastej (s uchetom tochnyh opredelenij tipov) stanovitsya osnovnym sposobom sotrudnichestva mezhdu otdel'nymi gruppami programmistov. |ti interfejsy yavlyayutsya osnovnym rezul'tatom processa proektirovaniya i sluzhat glavnym sredstvom obshcheniya mezhdu razrabotchikami i programmistami. Otkaz ot etogo privodit k proektam, v kotoryh neyasna struktura programmy, kontrol' oshibok otlozhen na stadiyu vypolneniya, kotorye trudno horosho realizovat' na S++. Rassmotrim interfejs, opisannyj s pomoshch'yu "ob容ktov", opredelyayushchih sebya samostoyatel'no. Vozmozhno, naprimer, takoe opisanie: "Funkciya f() imeet argument, kotoryj dolzhen byt' samoletom" (chto proveryaetsya samoj funkciej vo vremya ee vypolneniya), v otlichie ot opisaniya "Funkciya f() imeet argument, tip kotorogo est' samolet" (chto proveryaetsya translyatorom). Pervoe opisanie yavlyaetsya sushchestvenno nedostatochnym opisaniem interfejsa, t.k. privodit k dinamicheskoj proverke vmesto staticheskogo kontrolya. Analogichnyj vyvod iz primera s samoletom sdelan v $$1.5.2. Zdes' ispol'zovany bolee tochnye specifikacii, i ispol'zovan shablon tipa i virtual'nye funkcii vzamen neogranichennyh dinamicheskih proverok dlya togo, chtoby perenesti vyyavlenie oshibok s etapa vypolneniya na etap translyacii. Razlichie vremen raboty programm s dinamicheskim i staticheskim kontrolem mozhet byt' ves'ma znachitel'nym, obychno ono nahoditsya v diapazone ot 3 do 10 raz. No ne sleduet vpadat' v druguyu krajnost'. Nel'zya obnaruzhit' vse oshibki s pomoshch'yu staticheskogo kontrolya. Naprimer, dazhe programmy s samym obshirnym staticheskim kontrolem uyazvimy k sboyam apparatury. No vse zhe, v ideale nuzhno imet' bol'shoe raznoobrazie interfejsov so staticheskoj tipizaciej s pomoshch'yu tipov iz oblasti prilozheniya, sm. $$12.4. Mozhet poluchit'sya, chto proekt, sovershenno razumnyj na abstraktnom urovne, stolknetsya s ser'eznymi problemami, esli ne uchityvaet ogranicheniya bazovyh sredstv, v dannom sluchae S++. Naprimer, ispol'zovanie imen, a ne tipov dlya strukturirovaniya sistemy privedet k nenuzhnym problemam dlya sistemy tipov S++ i, tem samym, mozhet stat' prichinoj oshibok i nakladnyh rashodov pri vypolnenii. Rassmotrim tri klassa: class X { // pseudo code, not C++ f() g() } class Y { g() h() } class Z { h() f() } ispol'zuemye nekotorymi funkciyami bestipovogo proekta: k(a, b, c) // pseudo code, not C++ { a.f() b.g() c.h() } Zdes' obrashcheniya X x Y y Z z k(x,y,z) // ok k(z,x,y) // ok budut uspeshnymi, poskol'ku k() prosto trebuet, chtoby ee pervyj parametr imel operaciyu f(), vtoroj parametr - operaciyu g(), a tretij parametr - operaciyu h(). S drugoj storony obrashcheniya k(y,x,z); // fail k(x,z,y); // fail zavershatsya neudachno. |tot primer dopuskaet sovershenno razumnye realizacii na yazykah s polnym dinamicheskim kontrolem (naprimer, Smalltalk ili CLOS), no v S++ on ne imeet pryamogo predstavleniya, poskol'ku yazyk trebuet, chtoby obshchnost' tipov byla realizovana kak otnoshenie k bazovomu klassu. Obychno primery, podobnye etomu, mozhno predstavit' na S++, esli zapisyvat' utverzhdeniya ob obshchnosti s pomoshch'yu yavnyh opredelenij klassov, no eto potrebuet bol'shogo hitroumiya i vspomogatel'nyh sredstv. Mozhno sdelat', naprimer, tak: class F { virtual void f(); }; class G { virtual void g(); }; class H { virtual void h(); }; class X : public virtual F, public virtual G { void f(); void g(); }; class Y : public virtual G, public virtual H { void g(); void h(); }; class Z : public virtual H, public virtual F { void h(); void f(); }; k(const F& a, const G& b, const H& c) { a.f(); b.g(); c.h(); } main() { X x; Y y; Z z; k(x,y,z); // ok k(z,x,y); // ok k(y,x,z); // error F required for first argument k(x,z,y); // error G required for second argument } Obratite vnimanie, chto sdelav predpolozheniya k() o svoih argumentah yavnymi, my peremestili kontrol' oshibok s etapa vypolneniya na etap translyacii. Slozhnye primery, podobnye privedennomu, voznikayut, kogda pytayutsya realizovat' na S++ proekty, sdelannye na osnove opyta raboty s drugimi sistemami tipov. Obychno eto vozmozhno, no v rezul'tate poluchaetsya neestestvennaya i neeffektivnaya programma. Takoe nesovpadenie mezhdu priemami proektirovaniya i yazykom programmirovaniya mozhno sravnit' s nesovpadeniem pri poslovnom perevode s odnogo estestvennogo yazyka na drugoj. Ved' anglijskij s nemeckoj grammatikoj vyglyadit stol' zhe neuklyuzhe, kak i nemeckij s anglijskoj grammatikoj, no oba yazyka mogut byt' dostupny ponimaniyu togo, kto beglo govorit na odnom iz nih. |tot primer podtverzhdaet tot vyvod, chto klassy v programme yavlyayutsya konkretnym voploshcheniem ponyatij, ispol'zuemyh pri proektirovanii, poetomu nechetkie otnosheniya mezhdu klassami privodyat k nechetkosti osnovnyh ponyatij proektirovaniya. 12.1.4 Gibridnyj proekt Perehod na novye metody raboty mozhet byt' muchitelen dlya lyuboj organizacii. Raskol vnutri nee i rashozhdeniya mezhdu sotrudnikami mogut byt' znachitel'nymi. No rezkij reshitel'nyj perehod, sposobnyj v odnochas'e prevratit' effektivnyh i kvalificirovannyh storonnikov "staroj shkoly" v neeffektivnyh novichkov "novoj shkoly" obychno nepriemlem. V to zhe vremya, nel'zya dostich' bol'shih vysot bez izmenenij, a znachitel'nye izmeneniya obychno svyazany s riskom. YAzyk S++ sozdavalsya s cel'yu sokratit' takoj risk za schet postepennogo vvedeniya novyh metodov. Hotya ochevidno, chto naibol'shie preimushchestva pri ispol'zovanii S++ dostigayutsya za schet abstrakcii dannyh, ob容ktno-orientirovannogo programmirovaniya i ob容ktno-orientirovannogo proektirovaniya, sovershenno neochevidno, chto bystree vsego dostich' etogo mozhno reshitel'nym razryvom s proshlym. Vryad li takoj yavnyj razryv budet vozmozhen, obychno stremlenie k usovershenstvovaniyam sderzhivaetsya ili dolzhno sderzhivat'sya, chtoby perehod k nim byl upravlyaemym. Nuzhno uchityvat' sleduyushchee: - Razrabotchikam i programmistam trebuetsya vremya dlya ovladeniya novymi metodami. - Novye programmy dolzhny vzaimodejstvovat' so starymi programmami. - Starye programmy nuzhno soprovozhdat' (chasto beskonechno). - Rabota po tekushchim proektam i programmam dolzhna byt' vypolnena v srok. - Sredstva, rasschitannye na novye metody, nuzhno adaptirovat' k lokal'nomu okruzheniyu. Zdes' rassmatrivayutsya kak raz situacii, svyazannye s perechislennymi trebovaniyami. Legko nedoocenit' dva pervyh trebovaniya. Poskol'ku v S++ vozmozhny neskol'ko shem programmirovaniya, yazyk dopuskaet postepennyj perehod na nego, ispol'zuya sleduyushchie preimushchestva takogo perehoda: - Izuchaya S++, programmisty mogut prodolzhat' rabotat'. - V okruzhenii, bednom na programmnye sredstva, ispol'zovanie S++ mozhet prinesti znachitel'nye vygody. - Programmy, napisannye na S++, mogut horosho vzaimodejstvovat' s programmami, napisannymi na S ili drugih tradicionnyh yazykah. - YAzyk imeet bol'shoe podmnozhestvo, sovmestimoe s S. Ideya zaklyuchaetsya v postepennom perehode programmista s tradicionnogo yazyka na S++: vnachale on programmiruet na S++ v tradicionnom procedurnom stile, zatem s pomoshch'yu metodov abstrakcii dannyh, i nakonec, kogda ovladeet yazykom i svyazannymi s nim sredstvami, polnost'yu perehodit na ob容ktno-orientirovannoe programmirovanie. Zametim, chto horosho sproektirovannuyu biblioteku ispol'zovat' namnogo proshche, chem proektirovat' i realizovyvat', poetomu dazhe s pervyh svoih shagov novichok mozhet poluchit' preimushchestva, ispol'zuya bolee razvitye sredstva S++. Ideya postepennogo, poshagovogo ovladeniya S++, a takzhe vozmozhnost' smeshivat' programmy na S++ s programmami, napisannymi na yazykah, ne imeyushchih sredstv abstrakcii dannyh i ob容ktno-orientirovannogo programmirovaniya, estestvenno privodit k proektu, imeyushchemu gibridnyj stil'. Bol'shinstvo interfejsov mozhno poka ostavit' na procedurnom urovne, poskol'ku chto-libo bolee slozhnoe ne prineset nemedlennogo vyigrysha. Naprimer, obrashchenie k standartnoj biblioteke math iz S opredelyaetsya na S++ tak: extern "C" { #include <math.h> } i standartnye matematicheskie funkcii iz biblioteki mozhno ispol'zovat' tak zhe, kak i v S. Dlya vseh osnovnyh bibliotek takoe vklyuchenie dolzhno byt' sdelano temi, kto postavlyaet biblioteki, tak chto programmist na S++ dazhe ne budet znat', na kakom yazyke realizovana bibliotechnaya funkciya. Ispol'zovanie bibliotek, napisannyh na takih yazykah kak S, yavlyaetsya pervym i vnachale samym vazhnym sposobom povtornogo ispol'zovaniya na S++. Na sleduyushchem shage, kogda stanut neobhodimy bolee slozhnye priemy, sredstva, realizovannye na takih yazykah kak S ili Fortran, predstavlyayutsya v vide klassov za schet inkapsulyacii struktur dannyh i funkcij v interfejs klassov S++. Prostym primerom vvedeniya bolee vysokogo semanticheskogo urovnya za schet perehoda ot urovnya procedur plyus struktur dannyh k urovnyu abstrakcii dannyh mozhet sluzhit' klass strok iz $$7.6. Zdes' za schet inkapsulyacii simvol'nyh strok i standartnyh strokovyh funkcij S poluchaetsya novyj strokovyj tip, kotoryj gorazdo proshche ispol'zovat'. Podobnym obrazom mozhno vklyuchit' v ierarhiyu klassov lyuboj vstroennyj ili otdel'no opredelennyj tip. Naprimer, tip int mozhno vklyuchit' v ierarhiyu klassov tak: class Int : public My_object { int i; public: // definition of operations // see exercises [8]-[11] in section 7.14 for ideas // opredeleniya operacij poluchayutsya v uprazhneniyah [8]-[11] // za ideyami obratites' k razdelu 7.14 }; Tak sleduet delat', esli dejstvitel'no est' potrebnost' vklyuchit' takie tipy v ierarhiyu. Obratno, klassy S++ mozhno predstavit' v programme na S ili Fortrane kak funkcii i struktury dannyh. Naprimer: class myclass { // representation public: void f(); T1 g(T2); // ... }; extern "C" { // map myclass into C callable functions: void myclass_f(myclass* p) { p->f(); } T1 myclass_g(myclass* p, T2 a) { return p->g(a); } // ... }; V S-programme sleduet opredelit' eti funkcii v zagolovochnom fajle sleduyushchim obrazom: // in C header file extern void myclass_f(struct myclass*); extern T1 myclass_g(struct myclass*, T2); Takoj podhod pozvolyaet razrabotchiku na S++, esli u nego uzhe est' zapas programm, napisannyh na yazykah, v kotoryh otsutstvuyut ponyatiya abstrakcii dannyh i ierarhii klassov, postepenno priobshchat'sya k etim ponyatiyam, dazhe pri tom trebovanii, chto okonchatel'nuyu versii programmy mozhno budet vyzyvat' iz tradicionnyh procedurnyh yazykov. 12.2 Klassy Osnovnoe polozhenie ob容ktno-orientirovannogo proektirovaniya i programmirovaniya zaklyuchaetsya v tom, chto programma sluzhit model'yu nekotoryh ponyatij real'nosti. Klassy v programme predstavlyayut osnovnye ponyatiya oblasti prilozheniya i, v chastnosti, osnovnye ponyatiya samogo processa modelirovaniya real'nosti. Ob容kty klassov predstavlyayut predmety real'nogo mira i produkty processa realizacii. My rassmotrim strukturu programmy s tochki zreniya sleduyushchih vzaimootnoshenij mezhdu klassami: - otnosheniya nasledovaniya, - otnosheniya prinadlezhnosti, - otnosheniya ispol'zovaniya i - zaprogrammirovannye otnosheniya. Pri rassmotrenii etih otnoshenij neyavno predpolagaetsya, chto ih analiz yavlyaetsya uzlovym momentom v proekte sistemy. V $$12.4 issleduyutsya svojstva, kotorye delayut klass i ego interfejs poleznymi dlya predstavleniya ponyatij. Voobshche govorya, v ideale, zavisimost' klassa ot ostal'nogo mira dolzhna byt' minimal'na i chetko opredelena, a sam klass dolzhen cherez interfejs otkryvat' lish' minimal'nyj ob容m informacii dlya ostal'nogo mira. Podcherknem, chto klass v S++ yavlyaetsya tipom, poetomu sami klassy i vzaimootnosheniya mezhdu nimi obespecheny znachitel'noj podderzhkoj so storony translyatora i v obshchem sluchae poddayutsya staticheskomu analizu. 12.2.1 CHto predstavlyayut klassy? Po suti v sisteme byvayut klassy dvuh vidov: [1] klassy, kotorye pryamo otrazhayut ponyatiya oblasti prilozheniya, t.e. ponyatiya, kotorye ispol'zuet konechnyj pol'zovatel' dlya opisaniya svoih zadach i vozmozhnyh reshenij; i [2] klassy, kotorye yavlyayutsya produktom samoj realizacii, t.e. otrazhayut ponyatiya, ispol'zuemye razrabotchikami i programmistami dlya opisaniya sposobov realizacii. Nekotorye iz klassov, yavlyayushchihsya produktami realizacii, mogut predstavlyat' i ponyatiya real'nogo mira. Naprimer, programmnye i apparatnye resursy sistemy yavlyayutsya horoshimi kandidatami na rol' klassov, predstavlyayushchih oblast' prilozheniya. |to otrazhaet tot fakt, chto sistemu mozhno rassmatrivat' s neskol'kih tochek zreniya, i to, chto s odnoj yavlyaetsya detal'yu realizacii, s drugoj mozhet byt' ponyatiem oblasti prilozheniya. Horosho sproektirovannaya sistema dolzhna soderzhat' klassy, kotorye dayut vozmozhnost' rassmatrivat' sistemu s logicheski raznyh tochek zreniya. Privedem primer: [1] klassy, predstavlyayushchie pol'zovatel'skie ponyatiya (naprimer, legkovye mashiny i gruzoviki), [2] klassy, predstavlyayushchie obobshcheniya pol'zovatel'skih ponyatij (dvizhushchiesya sredstva), [3] klassy, predstavlyayushchie apparatnye resursy (naprimer, klass upravleniya pamyat'yu), [4] klassy, predstavlyayushchie sistemnye resursy (naprimer, vyhodnye potoki), [5] klassy, ispol'zuemye dlya realizacii drugih klassov (naprimer, spiski, ocheredi, blokirovshchiki) i [6] vstroennye tipy dannyh i struktury upravleniya. V bol'shih sistemah ochen' trudno sohranyat' logicheskoe razdelenie tipov razlichnyh klassov i podderzhivat' takoe razdelenie mezhdu razlichnymi urovnyami abstrakcii. V privedennom vyshe perechislenii predstavleny tri urovnya abstrakcii: [1+2] predstavlyaet pol'zovatel'skoe otrazhenie sistemy, [3+4] predstavlyaet mashinu, na kotoroj budet rabotat' sistema, [5+6] predstavlyaet nizkourovnevoe (so storony yazyka programmirovaniya) otrazhenie realizacii. CHem bol'she sistema, tem bol'shee chislo urovnej abstrakcii neobhodimo dlya ee opisaniya, i tem trudnee opredelyat' i podderzhivat' eti urovni abstrakcii. Otmetim, chto takim urovnyam abstrakcii est' pryamoe sootvetstvie v prirode i v razlichnyh postroeniyah chelovecheskogo intellekta. Naprimer, mozhno rassmatrivat' dom kak ob容kt, sostoyashchij iz [1] atomov, [2] molekul, [3] dosok i kirpichej, [4] polov, potolkov i sten; [5] komnat. Poka udaetsya hranit' razdel'no predstavleniya etih urovnej abstrakcii, mozhno podderzhivat' celostnoe predstavlenie o dome. Odnako, esli smeshat' ih, vozniknet bessmyslica. Naprimer, predlozhenie "Moj dom sostoit iz neskol'kih tysyach funtov ugleroda, nekotoryh slozhnyh polimerov, iz 5000 kirpichej, dvuh vannyh komnat i 13 potolkov" - yavno absurdno. Iz-za abstraktnoj prirody programm podobnoe utverzhdenie o kakoj-libo slozhnoj programmnoj sisteme daleko ne vsegda vosprinimayut kak bessmyslicu. V processe proektirovaniya vydelenie ponyatij iz oblasti prilozheniya v klass vovse ne yavlyaetsya prostoj mehanicheskoj operaciej. Obychno eta zadacha trebuet bol'shoj pronicatel'nosti. Zametim, chto sami ponyatiya oblasti prilozheniya yavlyayutsya abstrakciyami. Naprimer, v prirode ne sushchestvuyut "nalogoplatel'shchiki", "monahi" ili "sotrudniki". |ti ponyatiya ne chto inoe, kak metki, kotorymi oboznachayut bednuyu lichnost', chtoby klassificirovat' ee po otnosheniyu k nekotoroj sisteme. CHasto real'nyj ili voobrazhaemyj mir (naprimer, literatura, osobenno fantastika) sluzhat istochnikom ponyatij, kotorye kardinal'no preobrazuyutsya pri perevode ih v klassy. Tak, ekran moego komp'yutera (Makkintosh) sovsem ne pohodit na poverhnost' moego stola, hotya komp'yuter sozdavalsya s cel'yu realizovat' ponyatie "nastol'nyj" X, a okna na moem displee imeyut samoe otdalennoe otnoshenie k prisposobleniyam dlya prezentacii chertezhej v moej komnate. X YA by ne vynes takogo besporyadka u sebya na ekrane. Sut' modelirovaniya real'nosti ne v pokornom sledovanii tomu, chto my vidim, a v ispol'zovanii real'nosti kak nachala dlya proektirovaniya, istochnika vdohnoveniya i kak yakorya, kotoryj uderzhivaet, kogda stihiya programmirovaniya grozit lishit' nas sposobnosti ponimaniya svoej sobstvennoj programmy. Zdes' polezno predosterech': novichkam obychno trudno "nahodit'" klassy, no vskore eto preodolevaetsya bez kakih-libo nepriyatnostej. Dalee obychno prihodit etap, kogda klassy i otnosheniya nasledovaniya mezhdu nimi beskontrol'no mnozhatsya. Zdes' uzhe voznikayut problemy, svyazannye so slozhnost'yu, effektivnost'yu i yasnost'yu poluchennoj programmy. Daleko ne kazhduyu otdel'nuyu detal' sleduet predstavlyat' otdel'nym klassom, i daleko ne kazhdoe otnoshenie mezhdu klassami sleduet predstavlyat' kak otnoshenie nasledovaniya. Starajtes' ne zabyvat', chto cel' proekta - smodelirovat' sistemu s podhodyashchim urovnem detalizacii i podhodyashchim urovnem abstrakcii. Dlya bol'shih sistem najti kompromiss mezhdu prostotoj i obshchnost'yu daleko ne prostaya zadacha. 12.2.2 Ierarhii klassov Rassmotrim modelirovanie transportnogo potoka v gorode, cel' kotorogo dostatochno tochno opredelit' vremya, trebuyushcheesya, chtoby avarijnye dvizhushchiesya sredstva dostigli punkta naznacheniya. Ochevidno, nam nado imet' predstavleniya legkovyh i gruzovyh mashin, mashin skoroj pomoshchi, vsevozmozhnyh pozharnyh i policejskih mashin, avtobusov i t.p. Poskol'ku vsyakoe ponyatie real'nogo mira ne sushchestvuet izolirovanno, a soedineno mnogochislennymi svyazyami s drugimi ponyatiyami, voznikaet takoe otnoshenie kak nasledovanie. Ne razobravshis' v ponyatiyah i ih vzaimnyh svyazyah, my ne v sostoyanii postich' nikakoe otdel'noe ponyatie. Takzhe i model', esli ne otrazhaet otnosheniya mezhdu ponyatiyami, ne mozhet adekvatno predstavlyat' sami ponyatiya. Itak, v nashej programme nuzhny klassy dlya predstavleniya ponyatij, no etogo nedostatochno. Nam nuzhny sposoby predstavleniya otnoshenij mezhdu klassami. Nasledovanie yavlyaetsya moshchnym sposobom pryamogo predstavleniya ierarhicheskih otnoshenij. V nashem primere, my, po vsej vidimosti, sochli by avarijnye sredstva special'nymi dvizhushchimisya sredstvami i, pomimo etogo, vydelili by sredstva, predstavlennye legkovymi i gruzovymi mashinami. Togda ierarhiya klassov priobrela by takoj vid: dvizhushcheesya sredstvo legkovaya mashina avarijnoe sredstvo gruzovaya mashina policejskaya mashina mashina skoroj pomoshchi pozharnaya mashina mashina s vydvizhnoj lestnicej Zdes' klass Emergency predstavlyaet vsyu informaciyu, neobhodimuyu dlya modelirovaniya avarijnyh dvizhushchihsya sredstv, naprimer: avarijnaya mashina mozhet narushat' nekotorye pravila dvizheniya, ona imeet prioritet na perekrestkah, nahoditsya pod kontrolem dispetchera i t.d. Na S++ eto mozhno zadat' tak: class Vehicle { /*...*/ }; class Emergency { /* */ }; class Car : public Vehicle { /*...*/ }; class Truck : public Vehicle { /*...*/ }; class Police_car : public Car , public Emergency { //... }; class Ambulance : public Car , public Emergency { //... }; class Fire_engine : public Truck , Emergency { //... }; class Hook_and_ladder : public Fire_engine { //... }; Nasledovanie - eto otnoshenie samogo vysokogo poryadka, kotoroe pryamo predstavlyaetsya v S++ i ispol'zuetsya preimushchestvenno na rannih etapah proektirovaniya. CHasto voznikaet problema vybora: ispol'zovat' nasledovanie dlya predstavleniya otnosheniya ili predpochest' emu prinadlezhnost'. Rassmotrim drugoe opredelenie ponyatiya avarijnogo sredstva: dvizhushcheesya sredstvo schitaetsya avarijnym, esli ono neset sootvetstvuyushchij svetovoj signal. |to pozvolit uprostit' ierarhiyu klassov, zameniv klass Emergency na chlen klassa Vehicle: dvizhushcheesya sredstvo (Vehicle {eptr}) legkovaya mashina (Car) gruzovaya mashina (Truck) policejskaya mashina (Police_car) mashina skoroj pomoshchi (Ambulance) pozharnaya mashina (Fire_engine) mashina s vydvizhnoj lestnicej (Hook_and_ladder) Teper' klass Emergency ispol'zuetsya prosto kak chlen v teh klassah, kotorye predstavlyayut avarijnye dvizhushchiesya sredstva: class Emergency { /*...*/ }; class Vehicle { public: Emergency* eptr; /*...*/ }; class Car : public Vehicle { /*...*/ }; class Truck : public Vehicle { /*...*/ }; class Police_car : public Car { /*...*/ }; class Ambulance : public Car { /*...*/ }; class Fire_engine : public Truck { /*...*/ }; class Hook_and_ladder : public Fire_engine { /*...*/ }; Zdes' dvizhushcheesya sredstvo schitaetsya avarijnym, esli Vehicle::eptr ne ravno nulyu. "Prostye" legkovye i gruzovye mashiny inicializiruyutsya Vehicle::eptr ravnym nulyu, a dlya drugih Vehicle::eptr dolzhno byt' ustanovleno v nenulevoe znachenie, naprimer: Car::Car() // konstruktor Car { eptr = 0; } Police_car::Police_car() // konstruktor Police_car { eptr = new Emergency; } Takie opredeleniya uproshchayut preobrazovanie avarijnogo sredstva v obychnoe i naoborot: void f(Vehicle* p) { delete p->eptr; p->eptr = 0; // bol'she net avarijnogo dvizhushchegosya sredstva //... p->eptr = new Emergency; // ono poyavilos' snova } Tak kakoj zhe variant ierarhii klassov luchshe? V obshchem sluchae otvet takoj: "Luchshej yavlyaetsya programma, kotoraya naibolee neposredstvenno otrazhaet real'nyj mir". Inymi slovami, pri vybore modeli my dolzhny stremit'sya k bol'shej ee"real'nosti", no s uchetom neizbezhnyh ogranichenij, nakladyvaemyh trebovaniyami prostoty i effektivnosti. Poetomu, nesmotrya na prostotu preobrazovaniya obychnogo dvizhushchegosya sredstva v avarijnoe, vtoroe reshenie predstavlyaetsya nepraktichnym. Pozharnye mashiny i mashiny skoroj pomoshchi - eto dvizhushchiesya sredstva special'nogo naznacheniya so special'no podgotovlennym personalom, oni dejstvuyut pod upravleniem komand dispetchera, trebuyushchih special'nogo oborudovaniya dlya svyazi. Takoe polozhenie oznachaet, chto prinadlezhnost' k avarijnym dvizhushchimsya sredstvam - eto bazovoe ponyatie, kotoroe dlya uluchsheniya kontrolya tipov i primeneniya razlichnyh programmnyh sredstv dolzhno byt' pryamo predstavleno v programme. Esli by my modelirovali situaciyu, v kotoroj naznachenie dvizhushchihsya sredstv ne stol' opredelenno, skazhem, situaciyu, v kotoroj chastnyj transport periodicheski ispol'zuetsya dlya dostavki special'nogo personala k mestu proisshestviya, a svyaz' obespechivaetsya s pomoshch'yu portativnyh priemnikov, togda mog by okazat'sya podhodyashchim i drugoj sposob modelirovaniya sistemy. Dlya teh, kto schitaet primer modelirovaniya dvizheniya transporta ekzotichnym, imeet smysl skazat', chto v processe proektirovaniya pochti postoyanno voznikaet podobnyj vybor mezhdu nasledovaniem i prinadlezhnost'yu. Analogichnyj primer est' v $$12.2.5, gde opisyvaetsya svitok (scrollbar) - prokruchivanie informacii v okne. 12.2.3 Zavisimosti v ramkah ierarhii klassov. Estestvenno, proizvodnyj klass zavisit ot svoih bazovyh klassov. Gorazdo rezhe uchityvayut, chto obratnoe takzhe mozhet byt' spravedlivoX. X |tu mysl' mozhno vyrazit' takim sposobom: "Sumasshestvie nasleduetsya, vy mozhete poluchit' ego ot svoih detej." Esli klass soderzhit virtual'nuyu funkciyu, proizvodnye klassy mogut po svoemu usmotreniyu reshat', realizovyvat' li chast' operacij etoj funkcii kazhdyj raz, kogda ona pereopredelyaetsya v proizvodnom klasse. Esli chlen bazovogo klassa sam vyzyvaet odnu iz virtual'nyh funkcij proizvodnogo klassa, togda realizaciya bazovogo klassa zavisit ot realizacij ego proizvodnyh klassov. Tochno tak zhe, esli klass ispol'zuet zashchishchennyj chlen, ego realizaciya budet zaviset' ot proizvodnyh klassov. Rassmotrim opredeleniya: class B { //... protected: int a; public: virtual int f(); int g() { int x = f(); return x-a; } }; Kakov rezul'tat raboty g()? Otvet sushchestvenno zavisit ot opredeleniya f() v nekotorom proizvodnom klasse. Nizhe privoditsya variant, pri kotorom g() budet vozvrashchat' 1: class D1 : public B { int f() { return a+1; } }; a pri nizhesleduyushchem opredelenii g() napechataet "Hello, World" i vernet 0: class D1 : public { int f() { cout<<"Hello, World\n"; return a; } }; |tot primer demonstriruet odin iz vazhnejshih momentov, svyazannyh s virtual'nymi funkciyami. Hotya vy mozhete skazat', chto eto glupost', i programmist nikogda ne napishet nichego podobnogo. Delo zdes' v tom, chto virtual'naya funkciya yavlyaetsya chast'yu interfejsa s bazovym klassom, i chto etot klass budet, po vsej vidimosti, ispol'zovat'sya bez informacii o ego proizvodnyh klassah. Sledovatel'no, mozhno tak opisat' povedenie ob容kta bazovogo klassa, chtoby v dal'nejshem pisat' programmy, nichego ne znaya o ego proizvodnyh klassah. Vsyakij klass, kotoryj pereopredelyaet proizvodnuyu funkciyu, dolzhen realizovat' variant etoj funkcii. Naprimer, virtual'naya funkciya rotate() iz klassa Shape vrashchaet geometricheskuyu figuru, a funkcii rotate() dlya proizvodnyh klassov, takih, kak Circle i Triangle, dolzhny vrashchat' ob容kty sootvetstvuyushchih tipov, inache budet narusheno osnovnoe polozhenie o klasse Shape. No o povedenii klassa B ili ego proizvodnyh klassov D1 i D2 ne sformulirovano nikakih polozhenij, poetomu privedennyj primer i kazhetsya nerazumnym. Pri postroenii klassa glavnoe vnimanie sleduet udelyat' opisaniyu ozhidaemyh dejstvij virtual'nyh funkcij. Sleduet li schitat' normal'noj zavisimost' ot neizvestnyh (vozmozhno eshche neopredelennyh) proizvodnyh klassov? Otvet, estestvenno, zavisit ot celej programmista. Esli cel' sostoit v tom, chtoby izolirovat' klass ot vsyakih vneshnih vliyanij i, tem samym, dokazat', chto on vedet sebya opredelennym obrazom, to luchshe izbegat' virtual'nyh funkcij i zashchishchennyh chlenov. Esli cel' sostoit v tom, chtoby razrabotat' strukturu, v kotoruyu posleduyushchie programmisty (ili vy sami cherez nedelyu) smogut vstraivat' svoi programmy, to imenno virtual'nye funkcii i predlagayut elegantnyj sposob resheniya, a zashchishchennye chleny mogut byt' polezny pri ego realizacii. V kachestve primera rassmotrim prostoj shablon tipa, opredelyayushchij bufer: template<class T> class buffer { // ... void put(T); T get(); }; Esli reakciya na perepolnenie i obrashchenie k pustomu buferu, "zapayana" v sam klass, ego primenenie budet ogranicheno. No esli funkcii put() i get() obrashchayutsya k virtual'nym funkciyam overflow() i underflow() sootvetstvenno, to pol'zovatel' mozhet, udovletvoryaya svoim nuzhdam, sozdat' bufera razlichnyh tipov: template<class T> class buffer { //... virtual int overflow(T); virtual int underflow();