* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Objektove programovani v Turbo Pascalu * * ========================================================================= * * sepsal: Mircosoft (3. vydani) (http://mircosoft.mzf.cz) * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Obsah: 0 - Terminologie 1 - O co vlastne jde 2 - Deklarace objektu 3 - Deklarace metod 4 - Pristup k datovym polozkam (atributum) a metodam 5 - Dedicnost 6 - Dynamicke objekty 7 - Virtualni metody a polymorfismus (tento text doporucuji otevrit primo v prostredi TP, zapnout zvyraznovani syntaxe pro koncovku TXT a v pripade nejasnosti konzultovat napovedu) ****************************************************************************** 0 - Terminologie ****************************************************************************** Objekt - kombinovana struktura obsahujici jak data (jako treba record), tak i procedury a funkce Trida - objektovy typ (to, co definujeme v sekci type) Instance objektu - objektova promenna (to, co deklarujeme v sekci var) Atribut - datova polozka deklarovana uvnitr objektu Metoda - procedura nebo funkce deklarovana uvnitr objektu ****************************************************************************** 1 - O co vlastne jde ****************************************************************************** V beznem strukturovanem programovani pouzivame promenne a prikazy, pomoci kterych s promennymi manipulujeme. V objektove orientovanem programovani (OOP) pouzivame objekty, ktere vypadaji jako promenne, ale dokazi se o sebe postarat samy. Nekdy je to velmi uzitecne. S objektem muzeme provadet temer vsechno, co se zaznamem (record), a krome toho jeste spoustu dalsich veci. Vyhoda objektu je v tom, ze mame data pohromade s procedurami a funkcemi (metodami), ktere se o ne staraji. Vyrazne se snizuje riziko kolize identifikatoru (uvnitr objektu je samostatny jmenny prostor podobne jako treba uvnitr jednotky) a zlepsuje se prehlednost (nemusime vsechna data pracne posilat pres parametry apod.). Nevyhoda je, ze kdyz se zacne objektovy pristup prehanet a "objektivizuje" se vsechno za kazdou cenu, vznika mnozstvi problemu, ktere by v obycejnem strukturovanem programovani nikdy nenastaly a prehlednost pak opet klesa (prikladem muze byt treba Delphi). Nekomu tento pristup vyhovuje (a hlavne - - je to moderni!), ale ja osobne davam prednost zlate stredni ceste: objekty pouzivam jen kdyz tim dosahnu opravdu vyrazneho zprehledneni a zefektivneni prace. ****************************************************************************** 2 - Deklarace objektu ****************************************************************************** type nazev = object seznam atributu; seznam metod; Typ object vypada na prvni pohled end; podobne, jako typ zaznam (record). Je tu ale nekolik rozdilu: 1) Nemuzeme definovat primo promennou typu object (tedy napr. var Neco: object...), ale nejdriv musime definovat typ (type typ=object...) a az pak promennou tohoto typu (var Neco:typ;). 2) Typ objekt nejde definovat lokalne (v procedure nebo funkci). 3) Primo v objektu muzeme definovat procedury a funkce (metody). 4) Objekty po sobe mohou dedit - viz dale. 5) Promenne typu object nelze primo ukladat do souboru. Obecne tedy deklarace objektoveho typu - tridy - zacina hlavickou, podobnou jako pri deklaraci zaznamu, ale s klicovym slovem Object. Nasleduje seznam datovych polozek - atributu - jako u zaznamu (polozka:typ;). Pak nasleduji hlavicky metod (procedure neco1(parametry); function neco2...) a na konci je end; . Vzdycky piseme napred atributy a potom metody. Nekde dale v prislusnem programu nebo jednotce pak musime dodefinovat vsechny metody, jejichz hlavicky jsme zde uvedli. V jednotce se uvadi vzdy az v implementacni casti, v programu kdekoli, kde se da definovat normalni procedura nebo funkce. Ale vzdy jen globalne. (* Tohle nemusite znat: Existuji direktivy Private a Public. Pisi se pri deklaraci do tela objektu mezi jednotlive atributy a metody. Direktiva Private znamena, ze vsechno, co je za ni, nebude zvenci (z jineho modulu, tj. z jine jednotky nebo programu) pristupne. Public znamena, ze odtud dal je to pristupne odkudkoli. Pokud neuvedete nic, bere se to jako Public. Direktivy private a public muzete opakovat kolikrat chcete a v jakemkoli poradi. Jedinou podminkou zustava, ze uvnitr kazdeho useku musi byt vzdycky napred atributy a az potom metody. Priklad: type Neco = object z:integer; {pristupna odkudkoli} PRIVATE b:byte; {\ pristupne pouze ze stejneho modulu} procedure XXX; {/ } PUBLIC f:char; {\ pristupne odkudkoli} procedure YYY; {/ } end; var A:Neco; V programu tedy lze napsat: A.z:=2; A.yyy; A.f:='B'; Zapisy A.b:=2; nebo A.XXX; budou fungovat pouze pokud je volate ze stejneho modulu (tj. ze stejneho programu nebo jednotky, ve ktere je objekt definovan). Z jineho programu nebo jednotky jsou tyto atributy a metody nepristupne (neviditelne) a prekladac nahlasi "Unknown identifier". To jen tak pro uplnost, abyste se nelekli, az nekde tyhle veci uvidite. Kdyz zrovna nutne nepotrebujete zakazat primy pristup z programu do objektu deklarovanych v nejake jednotce, muzete na existenci techto direktiv klidne zapomenout. *) ****************************************************************************** 3 - Deklarace metod ****************************************************************************** Hlavicky metod deklarujeme uz v definici objektoveho typu (tridy): type obj = object x:byte; procedure p(x:integer); function f(a:word):char; end; Potom ale musime definovat, co vlastne maji delat. To se dela bud v bezne deklaracni casti programu (tam, kde definujeme normalni procedury a funkce) nebo v implementacni casti jednotky (kdyz je objekt definovan v jednotce). Pred jmeno metody pripojime tecku a jmeno objektu, ke kteremu patri: procedure obj.p(x:integer); {hlavicku opiseme} {bezna deklaracni cast} Begin {bezna prikazova cast} End; Diky tomuto systemu je mozne mit nekolik objektu, ve kterych budou stejne pojmenovane metody, a nazvy se nam nepotlucou. ****************************************************************************** 4 - Pristup k datovym polozkam (atributum) a metodam ****************************************************************************** K atributum objektu se dostaneme uplne stejne jako k polozkam zaznamu: pomoci identifikatoru prislusne instance objektu (promenne), tecky a nazvu polozky. Funguje i prikaz with. Z hlediska ortodoxni objektarske filosofie (paradigma zapouzdrenosti) by se primo k atributum zvenci pristupovat nemelo a o vsechno by se mely starat metody, ale proc si necim takovym zbytecne komplikovat zivot? :-) Metody vyvolame uplne stejne: nazev objektu, tecka a nazev metody plus pripadne parametry. Nebo take prikazem with. Pro to, kdy muzeme kterou metodu volat, plati stejna pravidla jako pro bezne procedury nebo funkce: proceduru jako prikaz, funkci jako vyraz (hodnotu) nebo s rozsirenou syntaxi ({$X+}) taky jako prikaz. Pokud uvnitr tela metody pouzivame nejaky atribut prislusneho objektu, piseme pouze jeho identifikator, bez jakychkoli tecek a identifikatoru trid. Pokud by z nejakeho duvodu hrozila kolize nazvu (napr. pokud uvnitr metody pouzijeme prikaz with na nejaky zaznam se stejnymi jmeny polozek), muzeme pouzit klicove slovo SELF a tecku, cimz rikame, ze myslime opravdu atribut z tohoto objektu, ve kterem je tato metoda definovana, a zadny jiny. Radeji priklad: {deklarace tridy (objektoveho typu):} type obj = object {atributy:} X,Y:integer; {metody:} procedure zadej(noveX,noveY:integer); function ukazX:integer; end; {definice metod:} procedure obj.zadej(noveX,noveY:integer); Begin X:=novex; {odkazuje se na atribut X tridy obj} self.Y:=novey; {odkazuje se na atribut Y, opet tridy obj (v tomto pripade je self celkem zbytecny)} End; function obj.ukazX:integer; Begin ukazx:=X; End; {jedna verejna funkce, ktera nema s tridou obj nic spolecneho:} function ukazX:integer; Begin writeln('X'); End; {deklarace instance objektu (objektove promenne):} var o:obj; BEGIN {prace s objektem:} o.zadej(3,5); {do atributu X promenne O se ulozi hodnota 3, do Y 5} writeln(o.Y); {primo vypise hodnotu atributu Y promenne O} writeln(o.ukazX); {vypise vysledek metody ukazX promenne O} writeln(ukazX); {vypise vysledek verejne funkce ukazX, ktera k objektu O nepatri} o.X:=10; {do atributu X promenne O primo vlozi hodnotu 10} with o do begin {zkraceny zapis s withem} x:=1; y:=2; writeln(ukazX); {ted se vola metoda objektu - jsme uvnitr with, takze je "lokalnejsi" nez verejna funkce ukazX} end; Pro volani metod objektu z jinych metod tehoz objektu plati totez co pro pristup k atributum: taky pouze jmeny tech metod. Proste jako by cela prikazova cast metody byla obalena prikazem "with self do...". Podobne, jako se odkazujeme na obsah objektu - teckovou notaci - se muzeme odkazovat i na obsah jednotek: repeat until crt.keypressed; graph.setcolor(7); apod.. Dobre pro pripad kolize identifikatoru, kdy nejaky lokalneji definovany prekryje ten globalni z dane jednotky. ****************************************************************************** 5 - Dedicnost ****************************************************************************** Dedicnost je dalsi silnou strankou objektu (a jednim ze zakladnich piliru celeho OOP). Kdyz chceme definovat zaznam, ktery by obsahoval vsechno, co jiny zaznam, a jeste k tomu neco navic, mame dve moznosti. Bud v novem zaznamu vsechno vypsat znovu, nebo do nej zaradit prvni zaznam jako polozku. V prvnim pripade spousta psani navic, v druhem komplikovany pristup pres dve tecky (a nekdy i vic). Objekty to maji jednodussi. Kdyz pri deklaraci tridy (objektoveho typu) v zavorce za jejim jmenem uvedeme jmeno jine, drive definovane tridy, zdedi tato nova trida (potomek) vsechny datove polozky i metody z te puvodni (predka). Pokud se jmeno nejake metody potomka shoduje se jmenem jine metody predka, nova metoda tu starou zakryje (tj. je v potomkovi "lokalneji" definovana). Takze muzeme mit dlouhy rodokmen objektu, ktere dedi jeden po druhem, a v kazdem jednu stejne pojmenovanou metodu (dejme tomu ZadejParametry nebo neco takoveho), ktera v kazde generaci dela neco uplne jineho (ma jine telo a/nebo i parametry). Pro atributy tohle neplati; pri pokusu o predeklarovani puvodniho atributu nejakym jinym (treba jineho typu) nam prekladac vynada hlasenim "duplicate identifier". Pokud se chceme z nejake metody potomka odkazat na zakrytou metodu predka, mame dve moznosti. Bud pouzijeme teckovou notaci (nazev_tridy.nazev_metody) nebo klicove slovo inherited (= zdedeno), ale tentokrat bez tecky (inherited nazev_metody). Rozdil mezi temito dvema zpusoby je v tom, ze slovem inherited se dostaneme vzdycky jenom "o jednu generaci vys", tj. na nejblizsi starsi verzi te metody, zatimco teckovou notaci zamerujeme primo jednotlive tridy a muzeme tedy vybrat kteroukoli verzi, treba pres pul rodokmenu daleko. Oba zpusoby ovsem funguji pouze uvnitr metod; zvenku se ke starsim verzim metod nedostaneme. Nejlepsi bude, kdyz si vsechno vysvetlime na jednoduchem priklade. {deklarace trid (typu):} type trida1 = object A:integer; procedure zadej(noveA:integer); procedura blabla; end; trida2 = object(trida1) {dedime komplet cely obsah typu trida1} B:integer; {novy atribut, ktery v prvni tride nebyl} procedure zadej(noveA,noveB:integer); {a nova verze metody} end; trida3 = object(trida2) {dedi vse po tride2} C:integer; procedure zadej(noveA,noveB,noveC:integer); procedure blabla; end; {implementace metod:} procedure trida1.zadej(noveA:integer); Begin A:=noveA; {tady je to jasne} End; procedura trida1.blabla; Begin writeln('prvni blabla'); End; procedure trida2.zadej(noveA,noveB:integer); {a nova verze metody} Begin {ted mame nekolik moznosti. Bud:} A:=noveA; {primo} {nebo:} inherited zadej(noveA); {od prvniho dostupneho predka} {nebo:} trida1.zadej(noveA); {primy odkaz na tridu1} {druhy atribut uz jinak nejde:} B:=noveB; End; {metoda blabla se do druhe tridy prenesla beze zmen} procedure trida3.zadej(noveA,noveB,noveC:integer); Begin {Opet mame vic naprosto rovnocennych moznosti. Bud:} a:=novea; b:=noveb; {nebo:} inherited zadej(noveA,noveB); {timto se dostaneme jen o jednu uroven vys, tedy na tridu2} {nebo:} trida2.zadej(noveA,noveB); {to same, jenom jinak zapsane} {nebo:} trida1.zadej(noveA); b:=noveB; {nebo se odkazeme az na tridu1 a B doplnime primo} {treti atribut uz se neda splest:} C:=noveC; End; procedure trida3.blabla; Begin write('treti blabla, '); inherited blabla; {timhle se dostaneme o jednu generaci vys, cili volame blabla z tridy2, ktera ji ma od tridy1} End; {deklarace instanci objektu (objektoych promennych):} var o1:trida1; o2:trida2; o3:trida3; {v programu:} o1.zadej(30); {tohle je jasne} o2.zadej(40,50); {ted volame druhou variantu ze tridy2} o3.zadej(1,2,3); {treti varianta ze tridy3} {o3.trida2.zadej(2,3), o3.inherited zadej a podobne konstrukce uz nejdou} o1.blabla; {vypise 'prvni blabla'} o2.blabla; {vypise take 'prvni blabla', protoze se zdedila} o3.blabla; {vypise 'treti blabla, prvni blabla'} {nebo s withem:} with o2 do begin zadej(1,1); blabla; end; To je asi tak vsechno, co se o zakladni variante dedicnosti da napsat. Vyhodna zacne byt dedicnost ve chvili, kdy misto zde uvedenych primitivnich metod Zadej budeme mit nejaky dlouhy a komplikovany kod - misto abychom ho psali porad znovu, prenese se do potomku cely a pouze neco malo doplnime a poupravime. Dedit muzeme vzdy jen po jedne tride, tj. v te zavorce za slovem object smi byt vzdy jen jeden identifikator. Dedenim muzou polozky objektu pouze pribyvat, nikdy se neztraceji. Potomek vzdycky obsahuje vsechno, co predek, nic nemuzeme ubrat. Kompatibilita prirazovani: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Muzeme bez omezeni prirazovat mezi instancemi stejne tridy, napr.: var a,b:trida1; ... a:=b; Instanci potomka muzeme priradit do instance predka: var predek:trida1; potomek:trida2; ... predek:=potomek; "Prebytecne" atributy (ty, ktere jsou v potomkovi, ale ne v predkovi) se proste neprekopiruji. Obracene (predek do potomka) to ale nejde, protoze by se do onech "prebytecnych" atributu nemelo co nakopirovat a zustaly by nedefinovane (prekladac nahlasi "Type mismatch"). ****************************************************************************** 6 - Dynamicke objekty ****************************************************************************** Az doted jsme se bavili o statickych objektech, tj. takovych, ktere deklarujeme v sekci VAR jako bezne promenne. Ale i objekty se daji alokovat dynamicky - za behu programu. Postup je stejny jako pri vytvareni dynamickych promennych typu record: definujeme si typ a ukazatel na nej, deklarujeme promennou toho ukazateloveho typu a pres ni alokujeme pamet procedurou New. Zruseni se provede obvyklym zpusobem procedurou Dispose. type objekt = object a:char; procedure proc; end; ukazatel = ^objekt; ... var u:ukazatel; BEGIN new(u); {vytvoreni dynamickeho objektu} u^.a:='X'; {pristup beznym zpusobem pres ^} u^.proc; {stejne tak u metod} dispose(u); {zruseni objektu} ... Stejne jako z recordu jdou i z objektu sestavovat spojove seznamy a podobne legracky. O ukazatelich a dynamickych promennych obecne jsem psal podrobneji v souboru UKAZ.TXT. Jestli jste necemu z vyse uvedeneho nerozumeli, doporucuji precist. ****************************************************************************** 7 - Virtualni metody a polymorfismus ****************************************************************************** Existuji dva zpusoby prace s metodami: 1) Brzka vazba (early binding) - metody jsou definovany staticky, jejich adresa je jasna uz v dobe prekladu. To je to, o cem jsme si dotedka povidali. Pokud neni metoda nijak specialne oznacena, povazuje se implicitne za statickou. 2) Pozdni vazba (late binding) - metody jsou dynamicke, jejich adresa se urcuje az za behu programu a je ulozena v tabulce virtualnich metod (virtual method table - VMT), ktera je soucasti prislusneho objektu. Dynamicke metody se oznacuji klicovym slovem VIRTUAL. Jak se to deklaruje: type objekt1 = object procedure StatickaMetoda; procedure VirtualniMetoda; virtual; end; Pri pozdejsi implementaci tela metody uz slovo virtual za hlavicku nepiseme. Pokud mame v programu treba jenom jeden objekt bez predku a bez potomku, je zbytecne se virtualitou zabyvat. Smysl to ma teprve kdyz se zacne dedit. Takze si pridame druhy typ: type objekt2 = object(objekt1) procedure StatickaMetoda; {prekryje puvodni StatickouMetodu} procedure VirtualniMetoda; virtual; {predefinuje puvodni VirtualniMetodu} end; var o1:objekt1; o2:objekt2; Pokud zdedime virtualni metodu a chceme ji v potomkovi predefinovat novou verzi, musime dodrzet dve podminky: - Nova verze teto metody musi byt take virtualni (jednou virtualni = navzdy virtualni). - Nova verze musi mit stejny pocet, typ a poradi parametru jako ta puvodni (ale nemusi se stejne jmenovat). Tela virtualnich metod definujeme uplne stejne jako u statickych. Take se uplne stejne volaji a stejnym zpusobem novejsi prekryva starsi a starsi verze se daji volat pres inherited nebo jmeno predka a tecku. Tak k cemu cely ten virtualni cirkus vlastne je? U statickych objektu (instance deklarovana v sekci VAR) je odpoved jednoducha: k nicemu. Ovsem situace se radikalne meni, pokud objekty vytvarime dynamicky (ukazatel + New a Dispose). K tomu, abychom mohli dynamicky alokovat objekt obsahujici jednu nebo vice virtualnich metod, musime postupovat trochu jinak nez u dynamickych recordu. Predne pro objekt musime definovat konstruktor. To je specialni metoda, ktera ma misto klicoveho slova procedure slovo CONSTRUCTOR. Navenek se chova jako normalni procedura, ale navic do ni prekladac prida kod pro inicializaci tabulky virtualnich metod (to nijak neovlivnime, deje se to plne automaticky). Konstruktor pak musime zavolat pred prvnim pouzitim takovehoto objektu, jinak se stane neco oskliveho ("Program provedl nepovolenou operaci a bude ukoncen..."). Protoze konstruktor tabulku virtualnich metod teprve vytvari, nesmi byt sam virtualni. Parametry muze mit libovolne. Nejlepsi bude opet predvest nejaky priklad: type objekt1 = object constructor init; {nezbytny konstruktor} procedure StatickaMetoda; procedure VirtualniMetoda; virtual; end; objekt2 = object(objekt1) {konstruktor se zdedil} procedure StatickaMetoda; procedure VirtualniMetoda; virtual; end; ukazatel1 = ^objekt1; ukazatel2 = ^objekt2; constructor objekt1.init; Begin {kod pro inicializaci VMT bude pridan automaticky, takze jestli nepotrebujeme, aby konstruktor jeste neco uzitecneho vykonal, muzeme ho nechat prazdny} End; procedure objekt1.StatickaMetoda; Begin writeln('staticka metoda prvniho objektu'); End; procedure objekt1.VirtualniMetoda; Begin writeln('virtualni metoda prvniho objektu'); End; procedure objekt2.StatickaMetoda; Begin writeln('staticka metoda druheho objektu'); End; procedure objekt2.VirtualniMetoda; Begin writeln('virtualni metoda druheho objektu'); End; var u1:ukazatel1; u2:ukazatel2; BEGIN {Ted si vytvorime dynamicke instance techto objektu:} new(u1); new(u2); {a hned je inicializujeme pomoci konstruktoru:} u1^.init; u2^.init; Jde to napsat i pohodlneji: new(u1,init); new(u2,init); kdy jako druhy parametr procedure New predame jmeno konstruktoru toho objektu, ktery vytvarime (i s pripadnymi parametry). Tohle je vyjimka specialne pro konstruktory, zadna jina metoda se takhle volat neda. Pise se to bez tecek a nazvu trid, prekladac to pochopi z typu prvniho parametru. Ted muzeme s obema objekty normalne pracovat: u1^.StatickaMetoda; {zobrazi se 'staticka metoda prvniho objektu'} u1^.VirtualniMetoda;{zobrazi se 'virtualni metoda prvniho objektu'} with u2^ do begin StatickaMetoda; {zobrazi se 'staticka metoda druheho objektu'} VirtualniMetoda;{zobrazi se 'virtualni metoda druheho objektu'} end; Zatim tedy porad nic napadneho. Ted se ale konecne dostavame ke zlatemu hrebu vecera :-). Provedeme prirazeni instance potomka do instance predka (vlastne ukazatelu na ne): u1:=u2; Mimochodem, muzete se presvedcit, ze obracene to nejde - rodic do potomka se proste priradit neda, dokonce ani kdyz jde o ukazatele, ktere mezi sebou obvykle byvaji libovolne kompatibilni (tady nejsou). Ukazatel u1, ktery je typu "ukazatel na predka", ted ukazuje na potomka. A ted znovu to, co jsme delali pred chvili: u1^.StatickaMetoda; {'staticka metoda prvniho objektu'} u1^.VirtualniMetoda;{'virtualni metoda DRUHEHO objektu' <- tady je rozdil} with u2^ do begin StatickaMetoda; {'staticka metoda druheho objektu'} VirtualniMetoda;{'virtualni metoda druheho objektu'} end; Adresy statickych metod se urcuji podle typu objektu, ze ktereho je volame. Takze StatickaMetoda se bude volat vzdycky podle typu "sveho" ukazatele nezavisle na tom, na co ten ukazatel ve skutecnosti ukazuje (i kdyby ukazoval na uplne jiny, z neho odvozeny typ). Adresy virtualnich metod se urcuji z tabulky VMT, ktera se pri prirazovani mezi objekty kopiruje stejne jako hodnoty atributu. Coz znamena, ze se vola virtualni metoda toho objektu, na kterou jsme zrovna dany ukazatel namirili, bez ohledu na to, jakeho je ten ukazatel typu. Tomu se rika polymorfismus a je to jeden ze zakladnich piliru celeho OOP. Z toho vyplyva: Muzeme si vyrobit datovou strukturu (seznam), ve ktere budeme mit objekty ruzneho typu. Vsechny budou mit jednoho spolecneho predka, ve kterem budou definovany zakladni atributy a sablony urcitych spolecnych metod. Seznam budeme obsluhovat pomoci ukazatele na tohoto predka. Pokud bychom pres tento ukazatel volali staticke metody, volaly by se jejich varianty definovane pro tohoto univerzalniho (abstraktniho) predka, coz znamena, ze bychom se z nich nemohli dostat k dalsim atributum ani metodam dodefinovanym v potomcich. Pokud ale pres tento ukazatel volame metody dynamicke, volaji se nove verze, ktere patri k tem potomkum, takze se z nich dostaneme na vsechny jejich nove atributy i metody. V tom predkovi, ze ktereho metody volame, sice jeste jejich tela definovana nebyla (nebo byla, ale asi trochu jinak), takze tou dobou nemohlo byt jasne, co budou v potomcich delat, ale maji stejny typ a pocet parametru (coz virtualni metody musi) a to je vsechno, co pocitac potrebuje k tomu, aby je mohl spravne zavolat. A protoze predek nemuze obsahovat vic veci nez potomek, nemuze volanim zadne predkovy metody z potomka dojit k zapisu do nealokovane pameti a podobne (at jiz pomoci statickych nebo virtualnich metod). Jeste zbyva zminit se o ruseni dynamickych objektu s virtualnimi metodami. Jako byl na inicializaci VMT konstruktor, je na jeji zruseni destruktor. Ten se oznacuje klicovym slovem DESTRUCTOR a prislusny kod do nej opet prida prekladac automaticky (takze jestli po nem nechceme jeste neco jineho, muzeme ho nechat prazdny). Destruktor volame bud samostatne tesne pred zrusenim objektu procedurou Dispose nebo jako jeji druhy parametr: dispose(u1,Done); kde Done je jmeno toho destruktoru. Samozrejme vas nikdo nenuti, abyste konstruktory vzdycky pojmenovanali Init a destruktory Done. To jsou jenom doporucene nazvy, ktere se pouzivaji asi nejcasteji, ale klidne si pouzivejte treba Vytvor a Zrus, to je jedno. Jinak o dynamickych objektech plati totez, co o jinych dynamickych promennych: kdyz je nezrusite (dispose) a ukazatel, ktery na ne ukazoval, nekam presmerujete, uz je nikdy nenajdete a budou zbytecne zabirat pamet az do ukonceni programu. ****************************************************************************** A to je vse, pratele... (c) Mircosoft (unor 2008)