Objektově orientované programování - otázky a odpovědi

Pája
Matfyz(ák|ačka) level I
Příspěvky: 10
Registrován: 10. 10. 2006 16:07
Typ studia: Informatika Mgr.
Kontaktovat uživatele:

Objektově orientované programování - otázky a odpovědi

Příspěvek od Pája »

Tady zveřejňuji přepis mailu, který si vyměnila studentka se svým cvičícím. Týká se nejasností v objektovém programování. Časem to přidám do učebnice Programování v příkladech (teď na to nemám moc času).

Ať je vám to k užitku.
> PRIVATE x PUBLIC tady jsem narazila na par nesrovnalosti, kdyz jsem
> porovnavala informace z ruznych zdroju a nevim jestli jsme to
> pochopila spravne. cilem ojektu by melo bbyt uchranit si sve datove
> polozky pred zasahy z venci, proto vetsina jazyku ma datove polozky
> nastaveny jako privatni.
Přesně tak.
> vyjimku tvori TP, ktery je ma verejne a
> utajit se nam je podari jen s pouzitim klicoveho slova PRIVATE.
Přesněji řečeno, implicitně jsou položky v TP veřejné. Tuto implicitní
ochranu lze změnit použitím slova "private". Pokud private neuvedeme,
třída se bude chovat, jako bychom u položky uvedli "public".
takze zalezi v jakem jazyce programuji???
Ano.
> asi kdyz ted pouzivam TP tak bych
> mela pouzivat PRIVATE.... taky jsem objevila, ze existuje taky
> neco jako PUBLIC, ale uz nebylo popsano kde a jak se to pouziva....
Public znamená, že položka je přístupná zvenčí (pokud nenapíšeme nic,
pak to má tentýž efekt, jako kdybychom napsali "public", tedy aspoň v
Pascalu).

Klíčové slovo "public" se hodí, pokud chceme v definici třídy uvést
nejprve soukromé a až za nimi privátní položky. Pak můžeme psát:

Kód: Vybrat vše

type zvire = object
private
    vek:     integer;
    hmot:    integer;
    bydl:    kontinent;
public
    constructor vyrob;
    procedure mluv;
end; {of object zvire}
> co
> by teda melo byt verejne a co soukrome???
Datové položky by veřejné neměly být nikdy. Veřejné musí být všechny
metody, u kterých chceš, aby je mohl volat uživatel. Naopak například
pomocné metody by měly být privátní.
> ja jsem to pochopila tak, ze
> uzivatel by se mel dostat jen k hlavicce metody, ale jak to funguje a
> jak se inicializuji, meni datove polozky by melo byt soukrome a jen v
> reziji programatora. jak se to ovsem ralizuje v praxi???
V příloze posílám "praktickou" ukázku použití.

> ABSTRAKTNI x VIRUALNI metody abstraktni metody ma kryl uvedeny v
> pozadavcich, ale ja jsem na ne nikde nenarazila. tudiz se domnivam, ze
> by to mohly byt ty virtualni.
Vedle jak ta jedle. Abstraktní metoda je metoda, která nemá tělo. Jenom
hlavičku. To znamená, že ji nelze volat (pochopitelně, co by se
provedlo, když nemá tělo...). Její tělo se definuje až v podtřídě (=
potomkovi, třídě, která z ní vznikne děděním). Abstraktní metoda má
smysl, pokud se nachází ve třídě, ze které neplyne, co přesně by metoda
měla dělat.

Tak například: Pokud máme třídu Okno a v ní metodu vykresliSe, je jasné,
že tato metoda smysl má, neboť každé okno se musí umět vykreslit. Není
však jasné, jak přesně by se okno mělo vykreslit. Jinak se totiž bude
vykreslovat okno pod Windows, jinak třeba okno v dosovském okně (takové
ty modré hnusy například v Turbo Pascalu). Proto třídě Okno vytvoříme
dva potomky: WindowsovskeOkno a DosovskeOkno. Obě tyto třídy budou dědit
od třídy Okno. Zatímco Okno neimplementovalo metodu vykresliSe, třídy
WindowsovskeOkno a DosovskeOkno už ano, protože víme, o jaký druh okna
se jedná a jak jej tedy vykreslit.

Poznámka: Pascal bohužel dovoluje vytvářet i objekty abstraktních tříd a abstraktní metody v Pascalu tělo mají - jeho úkolem je však vrátit chybu:

Kód: Vybrat vše

procedure mojeAbstraktniMetoda;
begin
    runerror(211);
end;
> tady me zaujala VMT, to ze se musi pred
> kazdym pouzitim virtualni metody inicializovat pomoci CONSTRUCTORU
> beru jako fakt, ale zajimalo by me, v cem ta inicializace spociva.
Není pravda, že se musí inicializovat před každým použitím virtuální
metody, stačí jen jednou, dříve, než se použije jakákoliv virtuální metoda.
> kazdy objekt ma jen jednu VMT pro vsechny svoje virtualni metody?
VMT neexistuje pro každý objekt, ale pro každou třídu. Každý objekt ale
obsahuje odkaz do tabulky virtuálních metod. A tento odkaz vyplní konstruktor.
> jak
> je velka, co kdyz budu mit spoustu vitualnich metod? (ciste
> hypoteticka otazka, protoze nepredpokladam, ze bych k tomu v praxi kdy
> dospela, ale vsude se pise, jak je VMT dulezita, al evic informaci o
> ni neni, tak by me to jen trochu zajimalo:)
Zkusím to popsat na příkladě. Mějme třídy:

Kód: Vybrat vše

Ctverec = object
   procedure vykresliSe; virtual;
   procedure nastavRozmery(rozmery: Dimension); virtual;
end;

ZaoblenyCtverec = object (Ctverec)
    procedure vykresliSe; virtual;
    nastavMiruZaobleni(zaobleni: Integer); virtual;
end;

VyplnenyCtverec = object (Ctverec)
    procedure vykresliSe; virtual;
    nastavBarvuVyplne(barva: Color); virtual;
end;

procedure Ctverec.vykresliSe;
begin
    for i := 1 to 4 do nakresliCaru;
end;

procedure Ctverec.nastavRozmery(rozmery: Dimension);
begin
    r := rozmery;
end;

procedure ZaoblenyCtverec.vykresliSe;
begin
    for i := 1 to 4 do nakresliCaru;
    zaobliRohy(z);
end;

procedure ZaoblenyCtverec.nastavZaobleni(zaobleni: Integer);
begin
    z := zaobleni;
end;

procedure vyplnenyCtverec.vykresliSe;
begin
    for i := 1 to 4 do nakresliCaru;
    vyplnVnitrek(b);
end;

procedure vyplnenyCtverec.nastavBarvuVyplne(barva: Color);
begin
    b := barva;
end;
Potom tabulka virtuálních metod pro první třídu bude vypadat takto (šipka ----> značí odkaz, ukazatel):

Kód: Vybrat vše

VMT_Ctverec:
(vykresliSe) --------------------> {for i := 1 to 4 do nakresliCaru;}
(nastavRozmery) -----------------> {r := rozmery}
Pro druhou třídu:

Kód: Vybrat vše

VMT_ZaoblenyCtverec:
(vyresliSe) ---------------------> {for i := 1 to 4 do nakresliCaru; zaobliRohy(z);}
(nastavZaobleni) ----------------> {z := zaobleni;}
(nastavRozmery) -----------------> {r := rozmery}
Pro třetí třídu:

Kód: Vybrat vše

VMT_VyplnenyCtverec:
(vyresliSe) ---------------------> {for i := 1 to 4 do nakresliCaru; vyplnVnitrek(b);}
(nastavBarvuVyplne) -------------> {b := barva;}
(nastavRozmery) -----------------> {r := rozmery}
Všimni si podstatné věci: pro každou virtuální metodu obsahuje tabulka
odkaz na implementaci této metody, která se má pro danou třídu volat.
Protože metoda vykresliSe je třídami ZaoblenyCtverec a VyplnenyCtverec
předefinovaná, míří v každé VMT odkaz pro vykresliSe jinam. Na druhou
stranu, metoda nastavRozmery předefinovaná není, takže v každé VMT míří
odkaz pro nastavRozmery na stejné místo.

A teď k úloze konstruktoru. Nechť objekt "a" vytvoříme takto:

1)

Kód: Vybrat vše

var a: Ctverec;
new(a, init);
....tady se zavolá konstruktor pro třídu Ctverec, a proto bude "a" obsahovat ukazatel na tabulku VMT_Ctverec.

2)

Kód: Vybrat vše

var b: ZaoblenyCtverec;
new(b, init);
....tady se zavolá konstruktor pro třídu ZaoblenyCtverec, a proto bude "b" obsahovat ukazatel na tabulku VMT_ZaoblenyCtverec.

Totéž pro vyplněný čtverec.

A pozor, to není vše! Pokud bychom napsali:

Kód: Vybrat vše

procedure maluj(var c: Ctverec);
begin
    c.vykresliSe;
end;

maluj(b); {<<< TOTO JE PODSTATNÉ !!!}
zavolá se metoda vykresliSe pro zaoblený čtverec, a to i přesto, že "c"
je typu Ctverec, nikoliv ZaoblenyCtverec. To proto, že "c" je sice typu
Ctverec, ale byl vytvořen konstruktorem pro ZaoblenyCtverec. Proto má v
sobě ukazatel na tabulku VMT_ZaoblenyCtverec.

> taky jse m narazila na DESTRUKTOR, ktery by se mel rusit virtualni
> metody. to ze je taky virtualni, je mi uplne jasne a doufam ze jsem
> vstrebala i rozdil mezi dispose a destruktorem. pokud mam dynamicky
> alokovanou promennou objektoveho typu a uz ji nepotrebuji, tak nejdrive
> pouziji destruktor a zrusim virualni metody a pak zavolam dipose, ktery
> smaze obsah promenne a uvolni pamet. jak si jeste matne vzpominam, tak
> kryl na prednasce uvadel, ze destruktor se nemusi pouzivat, ale asi k
> necemu slouzit bude, kdyz uz ho vytvorili, ne?
Destruktor slouží ke zrušení všeho, co objekt během svého života
napáchal. Tak například, dejme tomu, že implementuješ spojový seznam
objektově. Vytvoříš si objekt s názvem "seznam" a voláš na něm metody:

Kód: Vybrat vše

seznam.pridejNaZacatek(50);
seznam.pridejNaZacatek(5);
seznam.pridejNaZacatek(10);
seznam.pridejNaZacatek(4);
Každé přidání prvku na začátek způsobí, že se musí alokovat paměť. A
tato paměť se někdy musí opět uvolnit. A to je právě práce pro
destruktor. Pozor, dispose zavolané na objektu "seznam" samo o sobě
nepomůže, to zruší objekt "seznam" jako takový. Nezruší však to, co
objekt "seznam" naalokoval během svého života.

Nebo další příklad. Vytvoříme objekt typu "spojeni", který bude
reprezentovat síťové spojení. Zase, je slušností někdy spojení uzavřít
(analogie položení sluchátka) a to je právě práce pro destruktor!
> ABSTRAKTNI OBJEKT
> tak tady v tom plavu , ba se dokonce topim. nikde jsem na to nenarazila a moje poznamky z prednasky nestoji za nic.
> mohl bys mi to prosim objesnit?
Ne spíše abstraktní třída? Abstraktní třída je třída, která má aspoň
jednu abstraktní metodu. Ve výše uvedeném příkladu by například třída
Okno byla abstraktní, zatímco třídy WindowsovskeOkno a DosovskeOkno už
nikoliv. Abstraktní objekt... Pokud to opravdu existuje, pak se
domnívám, že je to objekt abstraktní třídy. To je taková úchylárna
Pascalu. Pascal umožňuje instanciovat i abstraktní třídy. Slušné
programovací jazyky toto nedovolí.
> DEDICNOST a PRIRAZOVANI
> tady mi neni uplne jasne jestli muzu promennou typu "potomek" priradit promenne typu "PREDEK" nebo naopak.
> napr. typ auto=object......
> typ osobni=object (auto)
> var felicie:osobni;
> male:auto;
>
> osobni je potomek auta a ma dalsi specificke metody. no a v prubehu programu budu treba potrebovat priradit do male:=auto....
> cimz ztratim specificke vlastnosti auta(metody co ma navic)....nebo to je nekorektni a funguje auto:=male???
> z toho jsem celkem zmatena
Platí toto: vždy lze přiřadit potomka do předka, nikoliv naopak. Takže:

Kód: Vybrat vše

zvire := kocka;
auto := dodavka;
lze.

Důvod: Takzvaný IS-A princip (van IS A car, cat IS A beast,...). Tedy,
protože kočka je zvíře, lze do proměnné typu zvíře přiřadit kočku. A
protože dodávka je auto, lze do proměnné typu auto přiřadit dodávku.
Naopak to nejde. Představ si toto:

Kód: Vybrat vše

zvire := pes;
kocka := zvire; {...kdyby to šlo}
Co jsme provedli? Právě jsme do proměnné typu kočka přiřadili psa! Z
tohoto důvodu je přiřazení typu kocka := zvire zakázané.

Je sice pravda, že přiřazením zvire := kocka se ztratí specifické
vlastnosti kočky, určitě pak nebude možné volat třeba metodu
zvire.mnoukej. To ale nevadí. Pokud programátor přižazuje do nadtřídy
(= předka), ví, co dělá, tedy je si vědom, že specifické vlastnosti
přiřazovaného objektu nebude potřebovat. Navíc, specifické vlastnosti
se neztratí úplně. Podívej se výš na příklad se čtverci. Konkrétně na
metodu "maluj". Tato metoda bere parametr typu Ctverec a přesto je
schopná kreslit i vyplněné či zaoblené čtverce. Proč? Volá totiž
virtuální metodu vykresliSe. A právě díky virtualitě to funguje.
> hrebicek do rakvicky mi jeste pridali SELF a INHERITED.....
Tak jeden příklad na self. Dejme tomu, že máš metodu nastavZaobleni
deklarovanou takto:

Kód: Vybrat vše

procedure nastavZaobleni(z: Zaobleni);
Srovnej s metodou nastavZaobleni, která byla deklarovaná výše v textu,
než budeš pokračovat dál!

V čem se liší? V nejedoznačnosti "z" (když se v těle zapíše "z", neví
se, jestli se tím rozumí členská proměnná "z" nebo parametr "z"). Tento konflikt se vyřeší napsáním:

Kód: Vybrat vše

procedure nastavZaobleni(z: Zaobleni);
begin
    self.z := z; {doufam, uz ten Pascal moc neumim...}
end;
A inherited? dokážu si představit dvě situace, kdy ho použít:

1) V předkovi máš metodu A a v potomkovi jsi ji předefinovala. Ale z
potomkovy metody dejme tomu B chceš volat metodu A, ale ne tu
předefinovanou, nýbrž tu původní, co byla v předkovi, pak ji musíš
kvalifikovat napsáním inherited.

2) V potomkovi předefinuješ metodu A a z této předefinované metody
chceš zavolat metodu A předka (třeba k metodě A chceš v potomkovi
přidat nějakou funkcionalitu, takže nejprve zavolaš původní předkovskou
metodu a pak provedeš další příkazy, které tam tu funkcionalitu
přidají). Nemůžeš předkovskou metodu ale volat pouhým napsáním A. To
bys (rekurzivně!) zavolala metodu A potomka. Proto musíš volání metody
A kvalifokovat připsáním inherited.
> SELF je odkaz, ze pouzivam metodu vlastni danemu objektu a INHERITED, ze pouzivam metodu meho predka...kdy se to vlastne pouziva???
>
> doufam, ze jsem ted nezavarila na gulas ja tobe:(
>
> taky se uz ucim dalsi teorii a mela bych jeste dotazek mimo objekty
> HODNOTA a REFERENCE:)
> jake jsou rozdilzy mezi predavani parametru hodnotou a refernci jsem pochopila, ale nejsem si uplne jista co je rychlejsi. me se jevi jako rychlejsi a pametove mene narocnejsi predavani hodnotou. protoze u predavani odkazem, se nejprve preda odkaz a pak se jeste musi najit adresu a pak pouzit informace. z cehoz usuzuji, ze jednoduch predani hodnoty, prirazeni informace do formalniho paramentru, je o dost jednodussi.
> co se nesmi predavat hodnotou a co se nesmi predavat odkazem???
Pomalejší je předávání hodnotou, a to proto, že hodnota se musí
kopírovat. Což o to, když je parametr typu Integer, tak to nevadí. A co
když je parametr typu "record" a ten obsahuje třeba dvacet položek? Pak
to kopírování opravdu zpomaluje.

Odkazem se předávat doporučuje:
Cokoliv, co je velké (record, objekt), tudíž kopírování hodnoty by zdržovalo.
Odkazem se předávat musí:
Proměnná, jejíž hodnotu chceme zevnitř volané funkce/procedury měnit.
Hodnotou se předávat doporučuje:
Cokoliv, co je malé (Integer) a hodnotu čehož nechceme z vnitřku funkce měnit.
Hodnotou se předávat musí:
Literály (tj. konstanty).
Toto by nefungovalo: procedure mojeProcedura(var cislo);
mojeProcedura(6378);
Důvod: při předávání odkazem se předává ODKAZ NA PROMĚNNOU. A 6378 není proměnná.
> INICIALIZOVANE promene (strukturovane konstanty)
> vim, ze jsem v cervnu na to nekde narazila, ale ted uz nejsem schopna to dohledat, mohl bys mi objasnit jeste tohle?
Například inicializované pole:

Kód: Vybrat vše

const d: array[1..n, a..n] of Integer = ((-1, 2), (1, 2), (2, 1), (2, -1), ... a tak dále ...);
Tímto deklarujeme 2rozměrné pole a hned ho naplníme (schválně, jestlipak víš, co je to za pole?).
mohl bys mi vysvetlit rozdil mezi tridou a
> objektem? jako tridu si predtavim zvirata a podtrida kockopes:P ale to
> jsou prece objekty, ne? nejak v tom nemam jasno, hotovy zverinec:)
Zásadní otázka, dobře, že ses zeptala. Třída je třeba Kocka, Pes, Okno.
Objekt je pak instance třídy, tedy pokud napíšeš:

Kód: Vybrat vše

var micka, cert, mourek, mikes: Kocka;
    bara, lumpik, kendy: Pes;
    chybovaHlaska, prihlasovaciDialog: Okno;
Tak micka, cert, mourek, mikes, bara, lumpik, kendy, chybovaHlaska,
prihlasovaciDialog jsou všechno objekty.


příloha

Kód: Vybrat vše

type kontinent = (AFRIKA, EVROPA, ANTARKTIDA);

type zvire = object
private
    vek:     integer;
    hmot:    integer;
    bydl:    kontinent;
public
    constructor vyrob;
    procedure mluv;
end; {of object zvire}


type pes = object (zvire)
private
    zuby:    integer;
public
    constructor vyrob;
    procedure mluv;
end; {of object pes}


constructor zvire.vyrob;
begin
    vek := 10;
    hmot := 5;
    bydl := EVROPA;
end;


procedure zvire.mluv;
begin
    writeln ('Mluvim.', vek: 10, hmot: 10);
end;


constructor pes.vyrob;
begin
    zvire.vyrob;
    zuby := 22;
end;


procedure pes.mluv;
begin
    writeln ('Haf!', zuby:10);
end;


type pZvire = ^zvire;
type pPes = ^pes;


var mojeZvire: pZvire;
var mujPes:    pPes;
begin
    new (mujPes);
    mujPes^.vyrob;
    mujPes^.mluv;
    writeln;
    mojeZvire := mujPes;
    mojeZvire^.mluv;
end.
Odpovědět

Zpět na „Programování 2“