Kßva? Nebo C++? Jak je libo...Miroslav Virius ╚lßnek pana ╚ady äNad kßvou bychom se nakonec shodli, ale jß mßm rad∞ji kakao...ô p°edstavuje hozenou rukavici; mn∞ nezb²vß, ne₧ ji zvednout. Ostatn∞ diskuse o vztahu C++ a Javy û a programovacφch jazyk∙ v∙bec û bude jist∞ zajφmavß nejen pro nßs dva. V n∞kter²ch mφstech budu citovat ·ryvky z Φlßnku pana ╚ady, ke kter²m se chci vyjßd°it. Citace budou sßzeny kurzφvou a odd∞leny od ostatnφho textu mezerou. Budou samoz°ejm∞ vytr₧enΘ z kontextu; ovÜem Φlßnek pana ╚ady najdete na tomto CD zßrove≥ s m²m, tak₧e pro vßs nebude problΘm si odpovφdajφcφ mφsto najφt a p°esv∞dΦit se, zda û a na kolik û jsou mΘ argumenty k v∞ci. C, C++, Java a uΦenφZaΦnu n∞Φφm, o Φem jsme dosud nehovo°ili, ale co pova₧uji za velkou v²hodu C++ ve srovnßnφ s ostatnφmi potomky jazyka C, jako je Objective C nebo Java. Programßtor, kter² znß jazyk C, m∙₧e prakticky ihned zaΦφt programovat v C++. Neobjektov∞, samoz°ejm∞, ale objektov² p°φstup se m∙₧e uΦit pr∙b∞₧n∞. K seznßmenφ s tφm, co z jazyka C v C++ chybφ nebo co m∙₧e mφt jin² v²znam, postaΦφ p°eΦφst si p∞tistrßnkov² Φlßnek û a to je zßle₧itost na 20 minut i s d∙kladn²m p°em²Ülenφm, o co jde. Velice podstatnΘ takΘ je, ₧e p°evß₧nou v∞tÜinu knihoven jazyka C lze pou₧φvat bez problΘm∙ v i C++. VÜe ostatnφ, co C++ nabφzφ, lze zvlßdat u₧ p°i prßci. Z toho ävÜeho ostatnφhoô je samoz°ejm∞ nejd∙le₧it∞jÜφ objektov² p°φstup. Zvlßdnutφ zßkladnφch syntaktick²ch a jin²ch pravidel pro objekty v C++ nenφ p°φliÜ nßroΦnΘ, daleko horÜφ je nauΦit se objektov∞ myslet, objektov∞ programovat. Zvlßdnout objektovou anal²zu a objektov² nßvrh programu; a k tomu je t°eba Φas, kterΘho programßtor û pokud ho programovßnφ opravdu ₧ivφ û nemß nikdy dost. V C++ lze programovat na jednΘ stran∞ zcela neobjektov∞, na druhΘ stran∞ prakticky Φist∞ objektov∞, a mezi t∞mito dv∞ma styly je mo₧n² tΘm∞° plynul² p°echod. Proces uΦenφ C++ lze popsat zdßnliv∞ paradoxnφ v∞tou: äCΘΦka° se C++ nauΦφ za t²den, ale uΦit se ho bude p∙l roku.ô V onom t²dnu je i aktivnφ zvlßdnutφ Üablon, prostor∙ jmen, v²jimek, dynamickΘ identifikace typ∙ a dalÜφch nßstroj∙, kterΘ v jazyce C nebyly. Jestli₧e posadφme cΘΦka°e k Jav∞, bude mu adaptace trvat p°ece jen o n∞co dΘle. I kdy₧ je Java û alespo≥ na prvnφ pohled û jednoduÜÜφ ne₧ C++, p°echod k nφ je pro cΘΦka°e slo₧it∞jÜφ, nebo¥ programßtor musφ ihned zm∞nit zp∙sob myÜlenφ. Navφc se musφ seznßmit s °adou knihovnφch t°φd, a p°itom mu zkuÜenost z jazyka C nenφ k niΦemu û s knihovnami zaΦφnß prost∞ od nuly. Je ovÜem pochopitelnΘ, ₧e naprosto jinak se bude na Javu a C++ dφvat programßtor, kter² znß n∞jak² objektov² jazyk, jako je t°eba Smalltalk. Jemu bude javsk² p°φstup znßm², nanejv²Ü mu bude v n∞kter²ch ohledech p°ipadat zbyteΦn∞ omezen². Na zßv∞r tohoto odstavce si dovolφm poznamenat, ₧e samoz°ejm∞ i v Jav∞ lze programovat neobjektov∞: Vytvo°φme metodu main a dalÜφ metody jako procedury svΘho programu a to vÜe z povinnosti uzav°eme do n∞jakΘ t°φdy. V²sledkem bude ryze procedurßlnφ program, i kdy₧ v tΘm∞° Φist∞ objektovΘm jazyku Java. N∞kterΘ uΦebnice Javy takhle zaΦφnajφ. I kdy₧ se v tΘto souvislosti vtφrß vzpomφnka na Φlßnek o skuteΦn²ch programßtorech a pojφdaΦφch kolßΦ∙, kter² tvrdil, ₧e äskuteΦn² programßtor dokß₧e napsat fortransk² program v jakΘmkoli jazyceô, nejde rozhodn∞ o projev profesionality û spφÜ je to projev nepochopenφ Javy a jejφ filozofie. P°et∞₧ovßnφP°et∞₧ovßnφ (Φi chcete-li overloading û ale jß dßvßm p°ednost Φesk²m termφn∙m, proto₧e pφÜu p°evß₧n∞ pro ΦeskΘ Φtenß°e) opravdu nenφ pro diskusi o objektov∞ orientovanΘm programovßnφ rozhodujφcφ. NicmΘn∞ naÜe povφdßnφ je o p°edevÜφm Jav∞ a C++, a proto sem pat°φ. P°et∞₧ovßnφ operßtor∙ je nßstroj, kter² b²vß v kontextu jazyka C++ s objektov²m programovßnφm spojovßn, ale rozhlΘdneme-li se po okolφ, zjistφme, ₧e ho poskytujφ i neobjektovΘ jazyky, jako je t°eba Fortran 90. Java p°et∞₧ovßnφ operßtor∙ ve skuteΦnosti obsahuje. Zamyslφme-li se nad v²razem a + b, zjistφme, ₧e jeho v²znam se liÜφ podle toho, zda jde o sΦφtßnφ dvou Φφsel typu int, dvou Φφsel typu double nebo o spojovßnφ dvou °et∞zc∙. Mßme tedy stejn²m symbolem oznaΦeno n∞kolik r∙zn²ch operacφ a p°ekladaΦ podle typu operand∙ urΦuje, kter² z operßtor∙ + pou₧ije. To znamenß, ₧e p°ekladaΦ Javy se s p°et∞₧ovßnφm operßtor∙ umφ vyrovnat, nedovoluje to vÜak obyΦejn²m programßtor∙m, ale jen tv∙rc∙m jazyka. V tom je C++ v∙Φi programßtorovi daleko poctiv∞jÜφ. Kdybychom zm∞nili v²znam t°eba operßtoru p°i°azenφ pro n∞kter² objektov² typ (t°φdu), mßme problΘm û s objekty toho typu pak prakticky nebude mo₧nΘ v∙bec pracovat, proto₧e nebude mo₧nost nijak nastavit hodnotu odpovφdajφcφ prom∞nnΘ! Vzhledem k tomu, ₧e Java pracuje s objekty pouze prost°ednictvφm referencφ, by opravdu nebylo rozumnΘ umo₧nit v nφ p°et∞₧ovßnφ p°i°azovacφho operßtoru. Podobn∞ by asi nebylo rozumnΘ dovolit p°et∞₧ovßnφ operßtoru teΦka a jeÜt∞ n∞kolika dalÜφch. Jen₧e to stßle nenφ d∙vod, proΦ p°et∞₧ovßnφ jako takovΘ nepodporovat. Koneckonc∙, C++ takΘ neumo₧≥uje p°et∞₧ovat vÜechny operßtory û mezi v²jimky pat°φ nap°. u₧ zmφn∞nß teΦka, pou₧φvanß podobn∞ jako v Jav∞ pro p°φstup k slo₧kßm instancφ. ...p°ece jen je syntaktickß anal²za zdrojovΘho textu bez mo₧nosti overloadovan²ch operßtor∙ snazÜφ; jeÜt∞ v∞tÜφ v²znam to mß u interpretovan²ch jazyk∙ û jako je (v∞tÜinou) prßv∞ Java. ZaΦnu trochu obecn∞ji: ╚asto se setkßvßm s v²hradami, ₧e p°et∞₧ovßnφ operßtor∙ je neefektivnφ nebo ₧e zp∙sobuje slo₧itost p°ekladaΦe. Prvnφ v²hrada je nesmyslnß: jsou-li a a b dv∞ instance objektovΘho typu, pro kter² je p°etφ₧en operßtor +, znamenß zßpis a
+ b totΘ₧ co operator+(a,
b) tedy volßnφ funkce (metody). Druhß v²hrada je oprßvn∞nß, alespo≥ ΦßsteΦn∞: M∙₧e-li programßtor p°et∞₧ovat operßtory, musφ s tφm p°ekladaΦ poΦφtat a v p°φpad∞ pot°eby um∞t podle jist²ch pravidel rozhodnout, kter² operßtor se mß pou₧φt, tj. kterou metodu mß zavolat. Nenφ to ovÜem o mnoho slo₧it∞jÜφ ne₧ rozliÜovßnφ p°etφ₧en²ch metod nebo vestav∞n²ch operßtor∙, a to Java umφ. SkuteΦnost, ₧e Java je interpretovan² jazyk, s tφm nemß nic spoleΦnΘho: Vφme p°ece, ₧e zdrojov² text se nejprve p°eklßdß do bajtovΘho k≤du, a teprve ten se interpretuje. Nepochybuji o tom, ₧e rozliÜovßnφ p°etφ₧en²ch metod v Jav∞ °eÜφ u₧ p°ekladaΦ, nikoli interpret JVM, a nenφ d∙vodu, proΦ by tomu tak nemohlo b²t i v p°φpad∞ operßtor∙. Na slo₧itosti bajtovΘho k≤du, stejn∞ jako na slo₧itosti JVM, se tφm nemusφ nic zm∞nit. A navφc, jak Φasto v dneÜnφm tΘm∞° bez v²jimky GUI sv∞t∞ opravdu vyu₧ijeme standardnφ v²stup? Jen v²jimeΦn∞; nesrovnateln∞ Φast∞ji pracujeme se stringy, pro kterΘ nßm overloading operßtoru << je mßlo platn²... Pomi≥me skuteΦnost, ₧e standardnφ v²stup se pou₧φvß nap°. p°i programovßnφ CGI skript∙, p°i lad∞nφ a v mnoha jin²ch situacφch û ono to opravdu nenφ mnoho ve srovnßnφ se zßplavou graficky orientovan²ch aplikacφ. Jen₧e objektovΘ datovΘ proudy jazyka C++ se v ₧ßdnΘm p°φpad∞ neomezujφ pouze na standardnφ vstupy a v²stupy. P°etφ₧enΘ operßtory >> a << se krom∞ standardnφch vstup∙ a v²stup∙ a prßce se soubory dajφ pou₧φt û a takΘ hojn∞ pou₧φvajφ û pro prßci se znakov²mi °et∞zci. Tyto äpam∞¥ovΘô proudy umo₧≥ujφ formßtovat data a v²sledek zapisovat do znakov²ch polφ. Stejn∞ dob°e umo₧≥ujφ Φφst data ze znakov²ch °et∞zc∙ naprosto stejn²m zp∙sobem jako ze souboru. M∙₧eme se na n∞ dφvat jako na analogii funkcφ sprintf() a sscanf() z jazyka C. Existujφ dokonce ve dvou implementacφch: Standardnφ t°φdy stringstream, istringstream a ostringstream jsou definovßny v hlaviΦkovΘm souboru <sstream>, ve starÜφch implementacφch jazyka C++ (a kv∙li zp∞tnΘ kompatibilit∞ takΘ ve v∞tÜin∞ nov∞jÜφch) najdeme proudy strstream, istrstream a ostrstream, kterΘ jsou definovßny v hlaviΦkovΘm souboru <strstrea.h>. A prßv∞ zde poskytujφ p°etφ₧enΘ operßtory >> a << pohodlφ, kterΘ mi v Jav∞ chybφ. Navφc, vzhledem k objektovΘ povaze datov²ch proud∙ umo₧≥ujφ tyto operßtory vstup a v²stup libovoln²ch datov²ch typ∙, i typ∙, kterΘ v dob∞ nßvrhu jeÜt∞ neznßme. P°et∞₧ovßnφ a polymorfismusZa dalÜφ, s overloadingem je problΘm i vinou polymorfismu. V pln∞ objektov²ch jazycφch dßvß velmi dobr² smysl t°eba pou₧φvat prom∞nnΘ pro obecnΘ, beztypovΘ objekty (id v Objective C, Object v Jav∞; v C++ to nenφ mo₧nΘ, ale teoretick²m ekvivalentem by mohl b²t void*). Overloading by proto musel b²t nikoli vlastnostφ p°ekladaΦe (jako je tomu v C++), ale runtime! No a? Pokusme se ale o seri≤znφ odpov∞∩. V tomto odstavci jde o dv∞ v∞ci: Za prvΘ o beztypovΘ kontejnery a za druhΘ o mo₧nost p°et∞₧ovßnφ operßtor∙ pro beztypovΘ instance. ZaΦneme druhou z nich, nebo¥ bezprost°edn∞ navazuje na p°edchozφ diskusi. Polymorfnφ chovßnφ objekt∙ b²vß implementovßno jako b∞hovß zßle₧itost, nejinak je tomu i v Jav∞ nebo v C++. Proto₧e vÜak pou₧itφ p°etφ₧enΘho operßtoru nenφ nic jinΘho ne₧ volßnφ metody, mohli bychom tvrdit, ₧e nejde o ₧ßdn² zvlßÜtnφ problΘm. Jen₧e on tu problΘm je, a ne mal²: V∞tÜina operßtor∙ je toti₧ binßrnφch a my bychom proto p°i zpracovßvßnφ objekt∙ v beztypov²ch kontejnerech pot°ebovali polymorfismus vzhledem k ob∞ma operand∙m; p°elo₧eno do jazyka volßnφ metod to znamenß, ₧e bychom pot°ebovali pozdnφ vazbu jak vzhledem k t°φd∞ instance, kterΘ pat°φ metoda, tak vzhledem k t°φd∞ parametru. To ovÜem neposkytuje Java ani C++. Na druhΘ stran∞ skuteΦnost, ₧e Java neposkytuje p°etφ₧enΘ operßtory, problΘm vφcenßsobnΘho polymorfismu nikterak ne°eÜφ. Jestli₧e pot°ebuji provßd∞t n∞jakΘ binßrnφ operace nad prvky beztypovΘho kontejneru, pak je jedno, zda tyto operace zapφÜu pomocφ operßtoru nebo jako volßnφ metody û vφcenßsobn² polymorfismus si musφm naprogramovat sßm. Pokud jde o samotnΘ beztypovΘ kontejnery, je celß zßle₧itost takΘ trochu slo₧it∞jÜφ, ne₧ by mohlo vypl²vat z p°edchozφho citßtu. P°edevÜφm, v Φist∞ objektov²ch jazycφch je obvyklΘ, ₧e vÜechny objekty jsou navzßjem p°φbuznΘ û majφ spoleΦnΘho p°edka, t°φdu, kterß se obvykle jmenuje Object a kterß implementuje spoleΦnΘ chovßnφ vÜech objekt∙. äBeztypov²ô kontejner obsahuje odkazy (reference) na instance typu Object, a proto₧e v objektovΘm programovßnφ m∙₧e potomek v₧dy zastoupit p°edka, lze do takovΘho kontejneru uklßdat opravdu tΘm∞° cokoli û p°esn∞ji jakΘkoli objekty, nebo¥ ty jsou instancemi potomk∙ t°φdy Object. (To je, alespo≥ v p°φpad∞ Javy, dost podstatnΘ; do äbeztypovΘhoô kontejneru v Jav∞ nem∙₧eme uklßdat hodnoty primitivnφch typ∙, nap°. Φφsel, pokud je äneobalφmeô do instancφ vhodn²ch t°φd.) äBeztypovostô tedy je n∞co, co v C++ nemß p°esnou analogii; t°φdy mohou b²t na sob∞ nezßvislΘ, mohou b²t Φleny mnoha r∙zn²ch d∞dick²ch hierarchiφ. Na druhΘ stran∞ mßlokdy pot°ebujeme kontejner, do kterΘho chceme uklßdat absolutn∞ cokoli, od Φφsel p°es okna po druhy ptakojeÜt∞r∙. Typicky pot°ebujeme polymorfnφ kontejner, kter² obsahuje bu∩ r∙znΘ druhy oken, nebo ty ptakojeÜt∞ry, ale ne obojφ. V C++ m∙₧eme samoz°ejm∞ pou₧φt kontejner zalo₧en² na beztypov²ch ukazatelφch (void*); nenφ to ale nejlepÜφ °eÜenφ, proto₧e do takovΘho kontejneru bychom mohli ulo₧it opravdu cokoli, Φφslo bot vedle ptakojeÜt∞ra, a tφm bychom se p°ipravili jednak o mo₧nost typovΘ kontroly u₧ p°i p°ekladu a za druhΘ o polymorfismus. (Museli bychom si spolu s ka₧d²m objektem uklßdat informaci o jeho typu a podle toho se pak orientovat p°i jeho zpracovßvßnφ.) OvÜem k tomu, abychom dosßhli stejnΘho v²sledku jako t°eba v Jav∞, staΦφ vytvo°it si vhodnou d∞dickou hierarchii s jednφm spoleΦn²m p°edkem, kter² se m∙₧e jmenovat t°eba takΘ Object, a pou₧φt kontejner, kter² m∙₧e obsahovat odkazy na instance tΘto t°φdy. Pokud od takovΘhoto spoleΦnΘho p°edka odvodφme jak ptakojeÜt∞ry, tak okna, budeme je tam sm∞t uklßdat vedle sebe, pokud ne, budeme muset mφt zvlßÜtnφ kontejner na okna a zvlßÜtnφ kontejner na ptakojeÜt∞ry; obojφ m∙₧e mφt svou logiku û zßle₧φ na tom, co vlastn∞ pot°ebujeme. Ostatn∞ °eÜenφ zalo₧enß na objektov²ch hierarchiφch se spoleΦn²m p°edkem byla obvyklou souΦßstφ r∙zn²ch knihoven, dodßvan²ch s p°ekladaΦi C++ na poΦßtku devadesßt²ch let. V souΦasnΘ dob∞ se ale zpravidla pou₧φvajφ kontejnery zalo₧enΘ na Üablonßch. Jejich v²hodou je, ₧e umo₧≥ujφ stejn∞ snadno pracovat s objektov²mi i s neobjektov²mi typy a p°itom v nich lze snadno omezit typ uklßdan²ch hodnot. Navφc jsou souΦßstφ standardnφ ÜablonovΘ knihovny jazyka C++. Komplexnφ a reßlnß ΦφslaP°iznßm se, ₧e poznßmku o nevhodnosti odvozovßnφ komplexnφch Φφsel jako potomka Φφsel reßln²ch jsem napsal jen jaksi na okraj, jako n∞co, co do ostatnφho textu vlastn∞ nezapadalo a Φemu jsem nep°iklßdal valnou d∙le₧itost. NicmΘn∞: Asi ka₧d² programßtor potvrdφ, ₧e b∞₧nß û a rßd bych zd∙raznil, ₧e naprosto rozumnß! û praxe vede p°esn∞ opaΦn²m sm∞rem: nejprve implementuji ten nejjednoduÜÜφ p°φpad a vÜe odladφm. Pak p°idßm n∞jakΘ rozÜφ°enΘ slu₧by, a zase vÜe odladφm... To znφ krßsn∞, ale pokud si n∞co podobnΘho vyzkouÜφte v p°φpad∞ komplexnφch Φφsel a pou₧ijete Javu nebo C++, nejspφÜ se dostanete do problΘm∙. Mno₧ina reßln²ch Φφsel je prost∞ podmno₧inou Φφsel komplexnφch, a proto je opaΦnß hierarchie doslova postavenß na hlavu. Na druhΘ stran∞ mi nezb²vß, ne₧ souhlasit, ₧e po strßnce implementace to svßdφ k uvedenΘmu postupu: Nejprve odladφme reßlnß Φφsla, pak p°idßme imaginßrnφ slo₧ku... P°edstava reßlnΘho Φφsla, kterΘ s sebou nosφ imaginßrnφ Φßst obsahujφcφ v₧dy nulu, se mi v∙bec nelφbφ a programßtor, kter² n∞co takovΘho spßchß, nejspφÜ sklidφ, co si zaslou₧φ. Postup, kter² uvßdφ pan ╚ada, rozhodn∞ mß jasnou logiku, ale vede v urΦit²ch situacφch k nesmyslnΘho chovßnφ programu. Tak₧e ob∞ cestu se z urΦitΘho ·hlu sice t°pytφ, ale zlatß nenφ ani jedna. NaÜt∞stφ C++ nabφzφ jinou cestu û sice na pohled mo₧nß mΘn∞ objektovou, ale o to elegantn∞jÜφ, kterß nenφ zalo₧ena na d∞diΦnosti. StaΦφ ve t°φd∞ komplexnφch Φφsel definovat konstruktor, kter² lze volat s jednφm parametrem typu reprezentujφcφho reßlnΘ Φφslo. Tak otev°eme cestu k automatick²m konverzφm reßln²ch Φφsel na Φφsla komplexnφ a nahradφme d∞diΦnost. Nynφ budeme moci na mφst∞ komplexnφho Φφsla bez problΘm∙ pou₧φt Φφslo reßlnΘ, zatφmco na mφst∞ reßlnΘho Φφsla Φφslo komplexnφ zapsat nep∙jde û tak, jak je to v matematice obvyklΘ. ...v mφstech, kde se oΦekßvß pouze reßlnΘ Φφslo, budeme moci pou₧φt i komplexnφ (avÜak vyu₧ije se pouze jeho reßlnß slo₧ka) û to nenφ nesmyslnΘ; Zde prosφm o p°φklad: mne ₧ßdn² rozumn² nenapadß. ZjiÜ¥ovßnφ typu instance za b∞huMusφm se up°φmn∞ p°iznat, ₧e neznßm zp∙sob, jak z tΘto pasti vyklouznout v rigidnφm jazyku typu C++, kde polymorfismus ani triky typu isKindOfClass nejsou k dispozici. Ale jsou. Polymorfismus je sice omezen na jednotlivΘ d∞dickΘ hierarchie, ale to je p°i Üφ°i mo₧nostφ, kterΘ tento jazyk poskytuje, p°irozenΘ û ostatn∞ v Jav∞ je tomu takΘ tak, nebo¥ tam vÜechny objekty tvo°φ jedinou hierarchii. (Ani Java nenφ pln∞ polymorfnφ: zkuste poslat n∞jakou zprßvu t°eba celΘmu Φφslu, nezapouzd°enΘmu do ₧ßdnΘ obalovΘ t°φdy.) A pokud jde o zjiÜ¥ovßnφ typu instance za b∞hu programu (n∞co jako isKindOfClass), k tomu slou₧φ v C++ standardnφ operßtor typeid, kter² umo₧≥uje dynamickou identifikaci typ∙. I tady û podobn∞ jako u operßtoru dynamic_cast û narazφme na omezenφ: aby fungoval opravdu ädynamickyô, musφ pracovat s polymorfnφmi t°φdami. OvÜem to je v p°φpad∞ jakΘkoli d∞dickΘ hierarchie vφcemΘn∞ samoz°ejmost. Vrßtφme-li se k p°edchozφmu p°φkladu, mohli bychom tedy i v C++ postupovat tak, ₧e definujeme nejprve t°φdu Real a od nφ odvodφme jako potomka t°φdu Complex. Na mφst∞, kde bychom cht∞li pouze reßlnß a nikoli komplexnφ Φφsla, bychom zkontrolovali typ p°edanΘho parametru a v p°φpad∞ nesouladu vyvolali nap°. v²jimku. Jen₧e ... nemohu se ubrßnit dojmu, ₧e to je stejn∞ krkolomnΘ °eÜenφ jako odvozovat reßlnß Φφsla od komplexnφch a tahat s sebou po°ßd nulovou imaginßrnφ Φßstφ. D∞diΦnost?Obßvßm se, ₧e p°φklad komplexnφch a reßln²ch Φφsel ukazuje, ₧e souΦasnΘ pojetφ d∞diΦnosti tak, jak je p°inßÜφ Java nebo C++, je v n∞kter²ch situacφch nevhodnΘ. ReßlnΘ Φφslo je zvlßÜtnφm p°φpadem komplexnφho Φφsla, ale takov²m, kde mß jedna slo₧ka nulovou hodnotu, a proto bychom ji pot°ebovali v odvozenΘ t°φd∞ vypustit. To je n∞co, co Java ani C++ neumφ û p°i d∞diΦnosti v jejich pojetφ lze datovΘ slo₧ky p°idßvat, nikoli ubφrat. P°itom nejde o p°φpad nijak ojedin∞l²: bod ve dvourozm∞rnΘm prostoru lze pova₧ovat za specißlnφ p°φpad bodu ve t°φrozm∞rnΘm prostoru, ve kterΘm je jedna slo₧ka nulovß, hada lze pova₧ovat za zvφ°e bez nohou atd. (Mßlokdo bude programovat obecnΘ zvφ°e jako hada s nohama.) PoznßmkaMyslφm, ₧e problΘm s reßln²mi a komplexnφmi Φφsly je takΘ odrazem skuteΦnosti, ₧e reßlnß Φφsla vlastn∞ nejsou zvlßÜtnφm p°φpadem komplexnφch Φφsel û alespo≥ ne v naprosto rigir≤znφm matematickΘm pojetφ. Mno₧ina komplexnφch Φφsel je mno₧inou uspo°ßdan²ch dvojic reßln²ch Φφsel. SamotnΘ reßlnΘ Φφslo v tΘto mno₧in∞ le₧et nem∙₧e û jedno Φφslo nenφ uspo°ßdanß dvojice Φφsel. Z praktickΘho hlediska je ovÜem v²hodnΘ prohlßsit, ₧e reßlnß Φφsla jsou prost∞ komplexnφ Φφsla, kterß majφ imaginßrnφ slo₧ku nulovou, a proto ji nepφÜeme. Toto zjednoduÜenφ je natolik v²hodnΘ, ₧e se o n∞m v b∞₧n²ch kurzech matematiky ani nemluvφ, p°edklßdß se jako samoz°ejmost û a v b∞₧n²ch matematick²ch aplikacφch to funguje. P°i objektovΘm nßvrhu to ale m∙₧e zp∙sobit potφ₧e. Virtußlnφ a nevirtußlnφ metodyJazyk C++ nabφzφ mimo jinΘ mo₧nost volat metodu äs plnou kvalifikacφô, nap°. u -> A::f(); Je-li f() virtußlnφ metoda, potlaΦφme tφm pozdnφ vazbu. Zde toti₧ °φkßme: Bez ohledu na to, jakΘho typu je instance, na kterou ukazuje u, chci metodu, kterß je definovßna ve t°φd∞ A. (Samoz°ejm∞ za p°edpokladu, ₧e u je ukazatel na potomka t°φdy A.) To nevypadß p°φliÜ objektov∞, ₧e? P°esto se to m∙₧e hodit. ObjektovΘ hierarchie nemusφ b²t navr₧eny v₧dy optimßln∞; p°edchozφ diskuse o reßln²ch a komplexnφch Φφslech to ukßzala dost jasn∞ û a to Ülo o pouhΘ dv∞ t°φdy, ve skuteΦn²ch objektov²ch knihovnßch jich mohou b²t stovky. Vedle toho se m∙₧e stßt, ₧e se pokusφme pou₧φt objektovou knihovnu k ·Φelu, kter² neodpovφdß p°esn∞ jejφmu p∙vodnφmu ·Φelu û i to je pom∞rn∞ b∞₧nß situace. U₧ to samo je d∙vod, proΦ m∙₧e b²t rozumnΘ podobnou mo₧nost nabφdnout. Podφvejme se ale na p°φklad, kter² by mohl pochßzet z libovolnΘho grafickΘho rozhranφ. ╖ P°edstavte si, ₧e mßme t°φdu Okno, kterß nabφzφ slu₧bu zobraz(). ObecnΘ okno se vykresluje v₧dy jako bφlß, Üed∞ orßmovanß plocha. ╖ Potomkem obecnΘ t°φdy okno bude t°φda PomocneOkno, kterΘ se vykresluje jako Üedß neorßmovanß plocha. Vedle toho PomocneOkno nabφzφ °adu dalÜφch slu₧eb, kterΘ v obecnΘm okn∞ nejsou û nap°φklad zm∞nu zp∙sobu zobrazenφ p°i stisknutφ tlaΦφtka myÜi. ╖ Od tΘto t°φdy bude odvozena celß °ada ovlßdacφch prvk∙ GUI, jako jsou tlaΦφtka, zaÜkrtßvacφ polφΦka ap. Mezi nimi bude i editaΦnφ pole û t°φda EditOkno û pro zadßvßnφ vstupu. Jen₧e editaΦnφ pole chceme nakreslit stejn∞ jako obecnΘ okno, tedy bφle s Üed²m orßmovßnφm. Zde urΦit∞ ocenφme mo₧nost implementovat metodu zobraz() pomocφ metody vzdßlenΘho p°edka. Jinak bychom m∞li nßsledujφcφ mo₧nosti: n Mohli bychom t°φdu EditOkno definovat jako p°φmΘho potomka t°φdy Okno. Pak bychom sice zd∞dili pot°ebnou implementaci metody zobraz(), ale ztratili bychom vÜechny specißlnφ vlastnosti, definovanΘ ve t°φd∞ PomocneOkno (a museli bychom je v nφ programovat znovu). Navφc bychom tφm t°φd∞ EditOkno dali zvlßÜtnφ postavenφ v rßmci celΘ hierarchie, a to takΘ nenφ dobrΘ. n Mohli bychom takΘ t°φdu EditOkno definovat jako potomka t°φdy PomocneOkno. Pak bychom ale museli p°edefinovat jejφ metodu zobraz() û a p°itom psßt znovu totΘ₧ co ve t°φd∞ Okno. V obou p°φpadech by to znamenalo, ₧e musφme urΦitΘ v∞ci programovat dvakrßt, a to nikdy nenφ dobrΘ. (M∙₧ete namφtnout, ₧e r∙znΘ zp∙soby kreslenφ lze naprogramovat jako jednu metodu, kterΘ p°edßme barvy plochy a rßmeΦku prost∞ jako parametry. Slo₧it∞jÜφ p°φklad by ale zabral p°φliÜ mnoho mφsta.) Lze se samoz°ejm∞ p°φt o tom, zda takovΘto °eÜenφ poruÜuje nebo neporuÜuje zapouzd°enφ. Domnφvßm se, ₧e ne. n SpoleΦn² p°edek û t°φda Okno û zve°ejnil, ₧e mß metodu zobraz(), kterß kreslφ bφlΘ, Üed∞ orßmovanΘ pole. (Nezve°ejnil jen nßzev metody, ale i v²sledek svΘ Φinnosti.) n Jejφ potomek, t°φda PomocneOkno, zve°ejnil, mß metodu zobraz(), kterß kreslφ ÜedΘ, Φern∞ orßmovanΘ pole. ProΦ by si potomek t°φdy PomocneOkno nemohl vybrat mezi implementacemi, kterΘ nabφzφ bezprost°ednφ a vzdßlen² p°edek? Ani jedno nep°edpoklßdß znalost vnit°nφ struktury objekt∙ û vyu₧φvßme pouze to, co o sob∞ zve°ej≥ujφ. (Informace o struktu°e d∞dickΘ hierarchie pat°φ, pokud vφm, k zßkladnφm informacφm o ka₧dΘ objektovΘ knihovn∞, a to nejen v C++, ale nap°. i v Jav∞.) Je mo₧nΘ samoz°ejm∞ namφtnout, ₧e lepÜφ by bylo vytvo°it pomocnou t°φdu, kterou pou₧ijeme jak v obecnΘm okn∞, tak v editaΦnφm poli. Jist∞; ale co kdy₧ dostanu t°φdy Okno a PomocneOkno ji₧ hotovΘ? To je p°ece naprosto b∞₧nß situace. ╚ist∞ virtußlnφ metoda s implementacφ je v∞c, kterß se asi p°φliÜ nepou₧φvß. Ve skuteΦnosti °ada i ÜpiΦkov²ch programßtor∙ pou₧φvajφcφch C++ o nφ nejspφÜ ani nevφ, ale souΦßstφ jazyka je, a proto jsem ji uvedl v p°ehledu mo₧nostφ, kterΘ C++ nabφzφ. Nejde o nic vφce, ne₧ o mo₧nost, jak potomk∙m nabφdnout mo₧nou implementaci a p°itom tuto metodu deklarovat jako Φist∞ virtußlnφ (Φi chcete-li abstraktnφ). C++ nabφzφ vedle sebe jak virtußlnφ, tak i nevirtußlnφ metody. Lze se nad tφm pozastavovat, lze s tφm nesouhlasit, ale to je asi tak vÜe, co s tφm lze d∞lat û n∞jak podobn∞ to °ekl Jßra Cimrman o n∞Φem ·pln∞ jinΘm. Jestli₧e o n∞jakΘ t°φd∞ nep°edpoklßdßm, ₧e by se mohla stßt p°edkem jinΘ t°φdy, pou₧iji v nφ nevirtußlnφ metody. Nu, v tom je prßv∞ ta chyba: nep°edpoklßdßme to, pravda û ale t°eba za dva roky ta situace nastane! V Epocu, kter² je kompletn∞ postaven na C++, se mi ji₧ mnohokrßt stalo, ₧e bych b²val pot°eboval mφrn∞ pozm∞nit chovßnφ n∞kterΘ standardnφ knihovnφ t°φdy... ale ouha, neÜlo to. To se m∙₧e stßt bohu₧el i v Jav∞. ╚as od Φasu zjistφm, ₧e bych pot°eboval p°edefinovat metodu p°edka a p°izp∙sobit ji k obrazu svΘmu, ale ouha û jejφ tv∙rce p°edpoklßdal, ₧e se m∞nit ji₧ nebude, nebo si to dokonce nep°ßl û a deklaroval ji jako final. Nebo tak deklaroval celou knihovnφ t°φdu û nap°φklad String. (Nechßpu, proΦ si od nφ nesmφm odvodit vlastnφho potomka, kter² by lΘpe vyhovoval m²m zßm∞r∙m, ale je to tak.) V obou jazycφch, a nejen v nich, se m∙₧e stßt, ₧e se p°edstavy tv∙rce knihovny a programßtora, kter² ji pou₧φvß, rozejdou. Bohu₧el je to v₧dy programßtor, kter² pak musφ psßt, psßt a psßt... Co je vlastn∞ t°φda a co objekt?V Φlßnku äP°ßtelskΘ nedorozum∞nφ nad kßvouô jsem napsal P°etypovßnφ (Object*)o p°ekladaΦi vlastn∞ °φkß: äZde mßÜ ukazatel o na t°φdu Interface; bu∩ tak laskav a zachßzej s nφm jako s ukazatelem na t°φdu Object.ô Jazyk C++ n∞co jako äukazatel na t°φduô ve skuteΦnosti neznß, a proto si mnoho programßtor∙ zkracuje dlouh² termφn a mφsto äukazatel na instanci t°φdy Objectô °φkß prost∞ äukazatel na t°φdu Objectô nebo äukazatel na Objectô. V diskusi o samotnΘm C++ by to nevadilo, ale my se bavφme takΘ o jazycφch, kde ukazatele na t°φdy skuteΦn∞ existujφ a to mohlo zp∙sobit nedorozum∞nφ. Pokud k n∞jakΘmu doÜlo, omlouvßm se. Vra¥me se ale k v²znamu uvedenΘho p°etypovßnφ. M∞li jsme dv∞ nezßvislΘ t°φdy, Object a Interface, a ukazatel na jejich spoleΦnΘho potomka. Pro lepÜφ orientaci si to zopakujeme: class
Interface {public: // Prvnφ nezßvislß t°φda
virtual void metoda()=0; }; class
Object { public: // Druhß nezßvislß t°φda
virtual void xxx() {
printf("Metoda vsech objektu...\n");
} }; class
Xxx: public Object, public Interface { public:
// SpoleΦn² potomek
virtual void metoda() {
printf("metoda tridy Xxx\n");
}; }; void
use(Interface* o) {
// P∙vodnφ implementace z Φlßnku äSilnß kßvaô ((Object*)o)->xxx();
// ! o->metoda(); } P°etypovßnφ, o kterΘ nßm jde, je vyznaΦeno tuΦn∞ a vyk°iΦnφkem v komentß°i. ProblΘm je, ₧e n∞co takovΘho bude v Jav∞ fungovat, ale v C++ ne. To nenφ chyba C++, ale programßtora. V C++ toto p°etypovßnφ opravdu znamenß prßv∞ to, co jsem cht∞l °φci u₧ v Φlßnku P°ßtelskΘ nedorozum∞nφ û uvedu poopravenΘ zn∞nφ svΘho tvrzenφ: äZde mßÜ ukazatel na instanci t°φdy Interface. Bu∩ tak laskav a zachßzej s nφm jako s ukazatelem na instanci t°φdy Object.ô Nic vφce a nic mΘn∞. Operßtor (typ) nevyu₧φvß dynamickou identifikaci typ∙ a tak nem∙₧e zjistit, zda je oprßvn∞nΘ p°ßnφ "mßme ukazatel na objekt, kter² je instancφ dvou t°φd zßrove≥ û t°φdy Interface a t°φdy Object. A₧ dosud jsme s nφm pracovali jako s instancφ Interface, nadßle s nφm chceme pracovat jako s instancφ Object" a pokud ano, kde by m∞l zd∞d∞nΘ slo₧ky Object vlastn∞ hledat. Tak₧e v²Üe uveden² citßt je za danΘ situace v C++ opravdu jen zbo₧nΘ p°ßnφ programßtora, kter² se nechal zmßst podobnostφ s Javou. Lze se ptßt, proΦ je to prßv∞ tak. Odpov∞∩ je jednoduchß û jde o d∞dictvφ jazyka C. Takovßto interpretace odpovφdß logice p°etypovßnφ ukazatele typu int* na ukazatel typu void* nebo naopak, p°etypovßnφ ukazatele typu int* na ukazatel typu unsigned* ap. Operßtor (typ) v C++ prost∞ nevyu₧φvß dynamickou identifikaci typ∙, s tφm se musφme smφ°it, a tam, kde ji pot°ebujeme, pou₧φt operßtor dynamic_cast. ProblΘm je, ₧e tv∙rci Javy p°evzali sice syntax jazyk∙ C a C++, ale dali jφ nov² v²znam. Proto m∙₧e totΘ₧ p°etypovßnφ v Jav∞ opravdu vyjad°ovat p°ßnφ uvedenΘ v citßtu. Jen₧e chceme-li n∞co podobnΘho °φci v C++, musφme pou₧φt prost°edky C++ a nenechat se zmßst podobnostφ s Javou. Jak je to s operßtorem dynamic_cast?U₧ minule jsem upozor≥oval, ₧e mφsto (typ) musφme pou₧φt operßtor dynamic_cast. P°etypovßnφ a dynamickΘ identifikaci typ∙ v C++ jsem v∞noval samostatn² Φlßnek, kter² vyÜel v Chipu 10/00 a 11/00, a proto zde pouze odpovφm na p°ipomφnky z Φlßnku pana ╚ady. ...jak "mlad²" je tento operßtor v C++? Jß se C++ uΦil zrovna z knihy pana Viriuse "Programovacφ jazyky C/C++" z roku 1992; mo₧nß jsem Üpatn∞ Φetl, ale s tφmto operßtorem jsem se v nφ nesetkal? Krom∞ toho, p°φklady zkouÜφm v GNU C verse 2.7.2.1 v Mac OS X Server û nenφ to samoz°ejm∞ nejnov∞jÜφ verse p°ekladaΦe, ale takΘ to nenφ nic prehistorickΘho! A kdy₧ jsem °eÜenφ s dynamic_cast cht∞l vyzkouÜet, se zlou jsem se potßzal... P°esnou odpov∞∩ bohu₧el neznßm. NicmΘn∞ mohu se pokusit alespo≥ o p°ibli₧nΘ vymezenφ doby, kdy se tento operßtor objevil v nßvrhu standardu jazyka C++. Knihu äProgramovacφ jazyky C/C++ô jsem napsal na podzim roku 1991, na trhu se objevila na ja°e 1992. V tΘ dob∞ se o tomto operßtoru jeÜt∞ nemluvilo. Na druhΘ stran∞ v knize B. Stroustrupa a M. A. EllisovΘ äThe Annotated C++ Reference Manualô, vydanΘ nakladatelstvφm Addison-Wesley v lednu 1994, ji₧ najdeme popis tohoto operßtoru v dodatcφch, kterΘ shrnujφ rozhodnutφ standardizaΦnφ komise. (Tato kniha slou₧ila dlouhou dobu jako neoficißlnφ norma C++. PoprvΘ vyÜla, pokud vφm v r. 1991.) V tΘ dob∞ se p°etypovacφ operßtory dynamic_cast, static_cast, reinterpret_cast a const_cast spolu s operßtorem typeid takΘ zaΦaly objevovat v p°ekladaΦφch. UrΦit∞ je obsahoval nap°φklad p°ekladaΦ Borland C++ 4.0, uvoln∞n² zaΦßtkem roku 1994. P°ibli₧n∞ v tΘ dob∞ se zaΦal objevovat takΘ v p°ekladaΦφch konkurenΦnφch firem. To znamenß, ₧e jeho specifikace je znßma ji₧ alespo≥ sedm let a jeho implementace v p°ekladaΦφch je k dispozici jen o n∞co kratÜφ dobu. NetuÜφm, proΦ p°ekladaΦ pana ╚ady neobsahuje tento operßtor a hlaviΦkov² soubor <typeinfo>, obßvßm se ale, ₧e to nenφ vina jazyka: Tento hlaviΦkov² soubor je p°edepsßn standardem (u₧ pom∞rn∞ dlouhou dobu) a proto by tam m∞l b²t û jako <typeinfo>, <typeinfo.h> nebo pod podobn²m jmΘnem. Ale na n∞co podobnΘho m∙₧eme narazit i v jin²ch programovacφch jazycφch. Jestli₧e se rozhodnu napsat aplet, m∙₧e se mi stßt, ₧e sice na mΘm poΦφtaΦi pob∞₧φ, ale webovΘ prohlφ₧eΦe v∞tÜiny u₧ivatel∙ budou hlßsit n∞co jako Class not found û nebyla nalezena pot°ebnß t°φda. To proto, ₧e jsem t°φdu svΘho apletu odvodil od t°φdy JApplet, jak se to doporuΦuje v Jav∞ 2, zatφmco v∞tÜina prohlφ₧eΦ∙ dosud podporuje pouze Javu 1.1, kterß t°φdu JApplet neznß. Mohu ale spadnout i do opaΦnΘ pasti: PouΦen Φetbou uΦebnic zasednu k poΦφtaΦi a napφÜu class
MujAplet extends JApplet {/* ... */} a p°ekladaΦ mi vynadß, ₧e n∞co takovΘho neznß, proto₧e mßm starÜφ verzi Javy. Programovacφ jazyky se vyvφjejφ, a to rychleji, ne₧ je nßm programßtor∙m milΘ, a nezb²vß, ne₧ to vzφt na v∞domφ. Overloading a ΦeÜtinaZde si dovolφm odboΦit od hlavnφho tΘmatu naÜφ diskuse a ocitovat slova pana ╚ady z Φlßnku äSilnß kßva pro objektovΘho programßtoraô. ...chcete-li, äp°etφ₧itô ù podle mΘho nßzoru je tento kalk z angliΦtiny dost zr∙dn², asi jako kdybychom cht∞li rozhranφ °φkat ämeziobliΦejô... Diskuse o ΦeskΘ terminologii v programovßnφ by vydala na samostatn² Φlßnek a jß se k n∞Φemu takovΘmu u₧ dlouhou dobu chystßm, ale zde si dovolφm jen n∞kolik struΦn²ch poznßmek. n P°et∞₧ovßnφ je doslovn² p°eklad (kalk) anglickΘho termφnu overloading, s tφm nelze nic d∞lat. Na druhΘ stran∞ nevidφm d∙vod, proΦ by se s tφm n∞co d∞lat m∞lo: Logika p∙vodnφho termφnu je asi tak äna toto jmΘno je nalo₧eno n∞kolik v²znam∙, je tedy p°etφ₧enoô. Na anglick² (p°esn∞ji americk²) termφn je to logickΘ a₧ moc û neobsahuje to narß₧ku na Alenku v °φÜi div∙, vtip ani slovnφ h°φΦku. n I kdy₧ to rozhodn∞ nenφ nejlepÜφ, nenφ to tak ÜpatnΘ a krom∞ toho, nic jinΘho se v ΦeÜtin∞ neujalo. Navφc rozhodneme-li se pro overloading, budeme muset overloadovat a °φkat dalÜφ p°φÜernosti. n Pou₧φvßnφ p∙vodnφ, tedy anglickΘ terminologie mi p°ipadß jako nejhorÜφ mo₧nost v∙bec. Termφn by m∞l pokud mo₧no vysv∞tlovat, a to i neznalΘmu, a to je mo₧nΘ jen v p°φpad∞, ₧e pou₧ijeme ΦeÜtinu. Aby bylo jasno, nebrßnφm se pou₧φvßnφ obecn∞ znßm²ch slov, kterß pochßzejφ z cizφch jazyk∙ a jsou blφzkß anglick²m termφn∙m n Anglickß terminologie znφ uΦen∞ a pomßhß äd∞lat z toho v∞duô. To pot°ebujφ filozofovΘ, kte°φ necht∞jφ, aby jim n∞kdo rozum∞l, proto₧e by mohl p°ijφt na to, ₧e jejich °eΦi jsou obsahov∞ prßzdnΘ. My si na nic podobnΘho hrßt nemusφme, programovßnφ a poΦφtaΦe mohou b²t pro °adu lidφ slo₧itΘ, i kdy₧ se budeme sna₧it to vysv∞tlit co nejsrozumiteln∞ji, a v²klad o n∞m rozhodn∞ nebude obsahov∞ prßzdn², i kdy₧ budeme mluvit Φesky. A pokud jde o ten meziobliΦej: To se opravdu nepou₧φvß, u₧ p°ed dvaceti nebo vφce lety n∞kdo vymyslel velice p∞kn² termφn rozhranφ, ale sßm jist∞ vφte, ₧e meziksicht je pom∞rn∞ oblφben² programßtorsk² vtφpek. Co dodatNa zßv∞r si dovolφm parafrßzovat v²rok, kter² svΘho Φasu °ekl W. Churchill o n∞Φem ·pln∞ jinΘm: C++ je nejhorÜφ mysliteln² jazyk. Bohu₧el, vÜechny ostatnφ jsou jeÜt∞ horÜφ.
|