Transcript oop

ELTE IK Informatika Doktori Iskola

Programozási nyelvek összehasonlító elemzése

Nyékyné Gaizler Judit INFPHD035-K-6 2009-2010-1

Objektumorientált programozás

Nyékyné Gaizler Judit (szerk.) et al.: Programozási nyelvek alapján szemináriumi előadás (vázlat) 2009. október 22.

Készítette: Székely László 2

Felhasznált források:

• Nyékyné Gaizler Judit (szerk.) et al.: Programozási nyelvek Egyetemi tankönyv, Budapest, 2003., Kiskapu • Angster Erzsébet: Az objektumorientált tervezés és programozás alapjai UML; Turbo Pascal; C++ 4 Kör Bt.,1999.

• Kotsis Domokos, Légrádi Gábor, Nagy Gergely, Szénási Ferenc: Többnyelvű programozástechnika Object Pascal, C++, C#, Java Panem, Budapest, 2007.

• http://nyelvek.inf.elte.hu/ 3

4

• Objektumorientált programozás a Programozási nyelvek könyv 12. fejezete • kiegészítések a könyvben nem szereplő nyelvek, újdonságok

5

12. Objektumorientált programozás

Valós világ modellezése 12.1. Az osztály és az objektum 12.1.1. Osztályok és objektumok az egyes nyelvekben 12.2. Jelölési módok, diagramok 12.2.1. Osztálydiagram 12.2.2. Objektumdiagram 12.2.3 A példányosítás ábrázolása 12.3. Objektumok létrehozása és megszűntetése 6

12.4. Egységbezárás (encapsulation) 12.5. Adatrejtés, interfészek 12.5.1. Friend metódusok, osztályok 12.6. Osztályadat, osztálymetódusok 12.7. Öröklődés 12.7.1. Adatrejtés és öröklődés 12.7.2. Polimorfizmus és dinamikus kötés 12.7.3. Absztrakt osztályok 12.7.4. Közös ős 12.7.5. Többszörös öröklődés 12.7.6. Interfészek 12.7.7. Beágyazott osztályok, tagosztályok 7

Valós világ modellezése

– OOP egy modellezési módszer, szemléletmód • módszertanok – Objektumorientáltnak nevezünk egy programot, mely egymással kapcsolatot tartó objektumok összessége.

8

12.1. Az osztály és az objektum

• Modellezés

– Absztrakció (elvonatkoztatás) – Megkülönböztetés – Osztályozás – Általánosítás – Specializáció (leszűkítés) – Részekre bontás – Kapcsolatok felépítése 9

12.1. Az osztály és az objektum

• Objektum – a modellezendő világ önálló egysége

– Statikus jellemzők

• Belső állapot • Neki küldhető üzenetek

(információkat tárol és műveleteket hajt végre) (adatok + metódusok)

• Azonosítás

10

12.1. Az osztály és az objektum

• Objektum – a modellezendő világ önálló egysége

– Dinamikus jellemzők

• Műveletek ütemezése • Állapotváltozások • Környezeti hatások • Kapcsolatok, kommunikáció, üzenetküldés

11

12.1. Az osztály és az objektum

• Osztály

– Ugyanolyan jellemzőket tartalmazó – Ugyanolyan viselkedésű objektumok csoportja – Objektum az osztály példánya (instance) – Absztrakt adattípus (reprezentációtól független) • Objektumok értékhalmaza • Elérhető műveletek 12

12.1.1. Osztályok és objektumok az egyes nyelvekben Nyelv SIMULA 67 Smalltalk ’new’ az osztály metódusa ’init’ az objektumé Osztály Class Név(…) Object subclass: #Név C++ class Név { } Object Pascal Eiffel Java C# Ada 95 Adatmezők Metódusok property „Osztályorientált”, osztálytól független objektumok nincsenek, az osztály nem objektum. Az osztály fordítási egység is. Csak az osztály a modularizáló eszköz Legkisebb önálló egység az osztály Adatmezők Metódusok property Hagyományos összetett adattípus is használható (Ada 83) Osztály – a rekordfogalom bővítése Csak attribútumokat tartalmaznak type Név=class class NÉV class Név{ } class Név{ } package Csomag is type Osztály is tagged private; … type Osztály is tagged record Objektum new Név(…) Minden objektum, az osztályok is: osztály adatok, osztály metódusok (vezérlő szerkezetek is) Osztály név; Osztály* név; Példányok létrehozása dinamikusan történik Felszabadítás: free var név:Osztály; Memória felszabadítás automatikus név: OSZTÁLY; !!név; Objektumok kezelése dinamikus Adattagok elérése set és get accessorok segítségével Osztály név=new Osztály(); Osztály név=new Osztály(); 13

12.1.1. Osztályok és objektumok az egyes nyelvekben C++ class Teglalap{ int x, y; public: void ertekadas(int, int); int terulet() {return (x*y);} }; 14

12.1.1. Osztályok és objektumok az egyes nyelvekben void Teglalap::ertekadas(int x1, int y1){ x = x1; y = y1; } Teglalap haz; //statikus helyfoglalású példány haz.ertekadas(5,3); int thaz = haz.terulet(); // thaz = 15 Teglalap* kert = new Teglalap; //dinamikus helyfoglalású példány kert->ertekadas(20,17); int tkert = kert->terulet(); //tkert = 340 15

12.1.1. Osztályok és objektumok az egyes nyelvekben

VB .NET

Class

Elem

Private

ePoint(2) as Integer

Default

Property Point(

ByVal

Integer)

As

Integer

Get Return ePoint(poz)

poz

End Get Set

(

ByVal

Value

As

Integer) ePoint(poz) = Value

As End Set End Property

...

End Class

16

12.2. Jelölési módok, diagramok

12.2.1. Osztálydiagram Osztály adat adat : típus adat : típus = érék … metódus metódus(paraméterlista) metódus : típus metódus(paraméterlista) : típus … 17

12.2. Jelölési módok, diagramok

12.2.2. Objektumdiagram obj : Osztály adat 1 adat 2 … adat n = érték 1 = érték 2 = érték n 18

12.2. Jelölési módok, diagramok

12.2.3 A példányosítás ábrázolása Osztály Osztály objektum multiplicitás objektum 19

12.3. Objektumok létrehozása és megszűntetése • Objektumok életciklusa: „megszületik”, „él”, „meghal” • Konstruktor: mint absztrakt adatszerkezetre beállítja a típus invariánst • Publikus osztály konstruktora is publikus legyen (kivéve pl.: singleton design pattern) 20

12.3. Objektumok létrehozása és megszűntetése Nyelv C++ Object Pascal Java Eiffel Ada 95 Konstruktor Konstruktor neve megegyezik az osztály nevével Konstruktor túlterhelhető Több konstruktor is lehet, nevük tetszőleges (TObject konstruktora Create), akármennyi paraméterük lehet. Objektum használata előtt külön meg kell hívni Konstruktor neve megegyezik az osztály nevével Konstruktor túlterhelhető Lehetnek paraméterei, de nem lehet visszatérési értéke A creation záradékkal adható meg a metódusok közül a konstruktor Tagged (jelölt) típus (Controlled, Limited_Controlled) felülírt Initialize metódusa lehet a konstruktor constructor Név(…); Destruktor Destruktor neve: ~Osztálynév Statikus és dinamikus objektum megszűnésekor automatikusan meghívódik, ezért nem lehet paramétere sem Destroy virtuális metódus az alapértelmezett destruktor Javasolt a Free hívása, ami csak nem nil objektumra hívja a Destroy-t.

A hivatkozást nekünk kell nil-re állítani Destruktor nincs, memória felszabadítás automatikus (garbage collection), előtte hívódik a finalize() metódus, osztály esetén a classFinalize() Nincs explicit destruktor, automatikus szemétgyűjtés Tagged (jelölt) típus (Controlled, Limited_Controlled) felülírt Finalize metódusa lehet a destruktor 21

12.3. Objektumok létrehozása és megszűntetése

Object Pascal

type

TŐs =

class

a, b: Integer;

constructor

...

c: Integer; Letrehoz(x, y: Integer);

end

;

type

TÚj =

class

(TŐs)

constructor

....

end

; Letrehoz(x, y, z: Integer); 22

12.3. Objektumok létrehozása és megszűntetése

Object Pascal

constructor begin

TÚj.Letrehoz(x, y, z: Integer);

inherited

Letrehoz(x, y); // a TŐs Letrehoz konstruktorát hívjuk

end

; c:=z; 23

12.3. Objektumok létrehozása és megszűntetése

Java

alapértelmezett konstruktor class ÉnOsztályom extends MásikOsztály { ÉnOsztályom() { super(); … } } 24

12.3. Objektumok létrehozása és megszűntetése A példányosítás és a Self (this) fogalma • Az objektum leírójában már nem szerepelnek a metódusok, csak az osztálynál.

• Több objektum esetén a metódusok egy az aktuális objektumra mutató hivatkozással oldják meg a hívó objektum azonosítását. Ez a híváskor általában rejtett Self (vagy this, vagy Current) paraméter.

• Objektum saját magának küldött üzenete esetén

Self.Üzenet(Paraméter)

a forma, legtöbb esetben nem kell kiírni sem, mert ez az alapértelmezett.

25

12.4. Egységbezárás (encapsulation)

• Adatstruktúrák + őket kezelő metódusok egysége és elrejtése • (Adatok is rejtve vannak, metódusok belső megvalósítása is) • Az absztrakt adattípusok elve – Specifikáció • Típusérték-halmaz + műveletek – Második szint • Reprezentáció • Implementáció – Osztály = típus (absztrakt adattípus) 26

12.4. Egységbezárás (encapsulation)

• Néhány nyelvben a rekord típus továbbfejlesztése az osztály (C++, Object Pascal) • Ada 95-ben az Ada 83 rekord fogalma bővült: osztály = tagged rekord és műveletek egy modulja (package) • A teljesen objektum orientált nyelveknél (pl.: Smalltalk, Java, Eiffel) nincs is direktszorzat, és unió típuskonstrukció, elegendő az osztály és az öröklődés.

27

12.5. Adatrejtés, interfészek

• Kapcsolat a külvilággal – interfész – Az objektum csak olyan üzenetekre reagál, amire megtanították – Az objektumot csak az interfészen keresztül lehet elérni – Az objektum interfésze a lehető legkisebb legyen – (Adatok csak a metódusokon keresztül legyenek elérhetők) 28

12.5. Adatrejtés, interfészek

• Interfész definiálása a programozó feladata: – Publikus adatok (esetleg csak lekérdezési lehetőséget jelent) – Publikus metódusok • Privát elérhetőségű adatokat, metódusokat csak az objektum használja • A láthatósági szabályok ellenőrzése fordítási időben történik 29

12.5. Adatrejtés, interfészek

Nyelv Smalltalk Objektum szintű láthatóság az adatokra Minden művelet mindig látható C++ Láthatóság osztályszintű Object Pascal Java private – csak forrásfájlon belül ha minden osztály külön fájlban van, akkor működik Osztályszintű láthatóság Adatok elérése property-n keresztül protected, csomagszintű public published private public private Eiffel Szelektív láthatóság objektum szintű láthatóság Egy osztály minden jellemzőjére megadható, hogy mely osztályok érhessék el public: ANY private: NONE 30

12.5.1. Friend metódusok, osztályok

• Objektum adataihoz való hozzáférés csak az interfészén keresztül lehetséges • C++ -ban ismertségi kapcsolat megvalósíthatósága miatt másik osztály metódusai hozzáférhetnek az adatokhoz: friend (barát) metódusok.

• Külső függvények, nem tudják a Self paramétert használni, ezért az aktuális objektum referenciáját külön paraméterként kell megadni • Egy osztálynak egy másik osztály is lehet „barát”-ja, ekkor annak metódusai elérik az osztály privát adatait is • C++ példa operátorok felüldefiniálása 31

12.6. Osztályadat, osztálymetódusok

• Objektumokkal dolgozunk • Objektumok adatai: példányadatok • Az objektum adatain dolgozó metódusok: példánymetódusok • Smalltalk: minden objektum  az osztály is objektum  valamilyen osztálynak a példánya  metaosztály • A metaosztály egyetlen lehetséges példánya az adott osztály • Minek az objektumai a metaosztályok (mint objektumok)?  A MetaClass nevű osztály objektumai metaosztály Osztály objektum MetaClass metaosztály Osztály objektum 32

12.6. Osztályadat, osztálymetódusok

• Az osztályok, mint objektumok is rendelkeznek adatokkal és metódusokkal • Az osztályok állapotát leíró adatok az osztályadatok • (C++, Java, C# esetén ezek a statikus adatok, és az objektumokon keresztül is elérhetők) • Az osztályadatokat manipuláló metódusok az osztálymetódusok • Az osztálymetódusok nem használhatják az objektumok adatait (nincs rejtett Self paraméterük) • (Ha nem léteznek objektumok, akkor is használhatók) Osztály.metódus(paraméterek), Osztály::metódus(paraméterek) 33

12.6. Osztályadat, osztálymetódusok

Nyelv Smalltalk Objektum instanceVariebleNames methods Osztály classVariableNames class methods C++, Java, C# Object Pascal static static class … Osztálydiagramokon osztályadat, osztálymetódus jelölése: C, vagy aláhúzás 34

12.6. Osztályadat, osztálymetódusok Java

public class Oszt { public Oszt(){ pldszam++; } static int pldszam=0; } public static int get_pldszam(){ return pldszam; }

35

12.6. Osztályadat, osztálymetódusok

Java public static void main(String[] args) { Oszt o1=new Oszt(); System.out.println( o1 .get_pldszam() + " az objszám " ); megegy(); System.out.println( osztv .get_pldszam() + " az objszám most " ); } protected static osztv megegy(){ Oszt o2= new Oszt(); return o2; }

36

12.7. Öröklődés

• Modellezés: Osztályok  alosztályok  al alosztályok … (specializálás), öröklési hierarchia • Öröklés: alosztályképzési művelet • Az alosztály örökli az ősosztály tulajdonságait • Egy változó tekinthető több osztály objektumának: polimorfizmus • Az osztály modulok egyszerre nyíltak és zártak 37

12.7. Öröklődés

Specializáció megvalósítása: – Új attribútum hozzáadása • (típusértékhalmaz szűkítésével jár) – Művelet (metódus) hozzáadása – Műveletek átdefiniálása (implementáció, esetleg specifikáció különbözősége) 38

12.7. Öröklődés

• Specifikációöröklődés (átdefiniált implementáció, polimorfizmus, dinamikus kötés) • Implementációs öröklődés (kód újra felhasználás): leszármazottban rejtetten használjuk az ősosztályt • Egyszeres öröklődés: egy közvetlen ősosztály • Többszörös öröklődés: több közvetlen ősosztály 39

12.7. Öröklődés

Nyelv SIMULA 67 első, amiben öröklődés volt prefix osztály prefix lánc egyszeres öröklés inner kulcssz ó Smalltalk C++ Object Pascal Eiffel subclass Java C# Ada 95 altípus reláció megvalósítása hivatalosan C++ hoz hasonló gyakorlatilag Java-hoz tagged rekord típusnak lehetnek leszármazottai inherit redefine extends (final) (sealed) többszörös öröklés egyszeres öröklés többszörös öröklés egyszeres öröklés egyszeres öröklés interfészek interfészek 40

12.7. Öröklődés

Simula 67 öröklődés: „prefixeléssel” rendeles class tetel_rendeles; begin integer tetel_meret; real feldolgozasi_ido; … létrehozáskor lefutó utasítások end; 41

12.7. Öröklődés

Object Pascal type TAllat = class public function eszik : string; private fajta : string; end; TKutya = class (TAllat) {TAllat leszármazottja} public function ugat : string; end; 42

12.7. Öröklődés

Object Pascal var Allat1 : TAllat; Kutya1 : TKutya; begin {...} Allat1 := Kutya1; {helyes, mert TAllat öse TKutyának} writeln(Allat1.eszik); writeln(Allat1.ugat); end.

{hibás, mert TAllat-ban nincs ugat metódus} writeln(Kutya1.ugat); {helyes} {...} 43

12.7.1. Adatrejtés és öröklődés

• adatrejtési módok – public +, – private -, – protected # • A privátnak deklarált adatok is öröklődnek, csak láthatatlanul • Láthatósági szint a leszármazottban átdefiniálással bővíthető 44

12.7.1. Adatrejtés és öröklődés

Öröklési módok • C++-ban a leszármazottban felülbírálhatjuk az ősbeli adatrejtést • A legtöbb nyelvben a nyilvános öröklési mód az alapértelmezett • C++: private (alapértelmezett), public, protected öröklési mód van • Eiffel-ben a leszármazott tetszőlegesen változtathatja az őstől örökölt jellemzők láthatóságát 45

12.7.2. Polimorfizmus és dinamikus kötés

• Specifikáció öröklődés: helyettesíthetőség (is a …, reláció) – A származtatott osztály objektumai felvehetik az ősosztály szerepét (nincs típuskeveredés) – Ahol az ősosztály formális paraméter, ott az utódosztály lehet aktuális paraméter – Ős (specializáció, altípus-képzési művelet), Ős   utód szűkítés utód általánosítás 46

12.7.2. Polimorfizmus és dinamikus kötés

• Megvalósítás: változókat típusnélkülinek tekintjük, tetszőleges értékadás megengedett (pl.: Smalltalk) • Másik lehetőség: statikus és dinamikus típus fogalma (változók polimorfizmusa) – (az OO polimorfizmus az altípusos polimorfizmus) – Statikus típus: osztály, amit a deklarációkor kapott – Dinamikus típus: a statikus típus, vagy annak tetszőleges leszármazottja (értékadáskor kapja) – A tisztán OO nyelvekben ezért azonosítjuk az objektumokat referenciákkal • Dinamikus kötés, virtuális művelet 47

12.7.2. Polimorfizmus és dinamikus kötés

• Metódusok átdefiniálása a leszármazottban – Metódus átdefiniálásának lehetősége <> metódusnevek túlterhelésének lehetősége • Statikus és dinamikus átdefiniálás – Object Pascal, C++ metódusok alaphelyzetben statikus kötésűek: dinamikus kötéshez a metódust virtuális metódusként kell megjelölni (VMT virtuális metódus tábla) – Smalltalk, Java, Eiffel alapértelmezetten dinamikus kötésűek 48

12.7.2. Polimorfizmus és dinamikus kötés

• Altípusok metódusai – Metódusokat jellemzi a szignatúrájuk: paramétereinek típusa, eredmény típusa, elő és utófeltétel •

ftt

= proc(típus) returns típus •

fat

= proc(altípus) returns típus •

fta

= proc(típus) returns altípus •

faa

= proc(altípus) returns altípus 49

12.7.2. Polimorfizmus és dinamikus kötés

• Átdefiniálásnál az eredmény lehet: azonos (novariáns) vagy speciálisabb (kovariáns) • A paraméter típusa csak azonos (novariáns) vagy kevésbé speciális (kontravariáns) lehet.

• tehát: – –

fta ftt

 

ftt

-

fat

az eredmény lehet speciálisabb a paraméterek lehetnek kevésbé speciálisak • Az utófeltétel maradhat változatlan, vagy szűkíthető • Az előfeltétel maradhat változatlan, vagy gyengíthető 50

12.7.2. Polimorfizmus és dinamikus kötés

Nyelv C++ Object Pascal Java C# Eiffel Dinamikus kötés Polimorfizmus paraméterek eredmény novariáns kovariáns csak referenciák esetén működik helyesen változók referenciák virtual virtual változók referenciák (polimorfizmus alapértelmezés) objektum változók referenciák, de a metódusok lehetnek statikusak és dinamikusak is változók referenciák, műveletek hívása dinamikusan történik virtual felüldefiniálás: override túlterhelés: overload virtuális metódus túlterheléses eltakarása: reintroduce mindig a dinamikus típusnak megfelelő metódust hívja felüldefiniálás: override túlterheléshez nincs külön kulcsszó virtuális metódus túlterheléses eltakarása: new felüldefiniálás: redefine előfeltételt csak gyengíteni, utófeltételt csak szűkíteni lehet Műveletek paraméterei lehetnek kovariánsok, emiatt futási hibák keletkezhetnek!

51

12.7.2. Polimorfizmus és dinamikus kötés

• Az ősosztály metódusainak meghívása – Egyszeres öröklés esetén az ősosztály elérése valamilyen kulcsszóval történhet.

Nyelv Object Pascal inherited előtte és utána is lehetnek utasítások Smalltalk Java C# C++ Eiffel super super base Ősosztály::Metódus( …) sehol nem szerepelhet hivatkozás az ősosztályra ismételt öröklődés: egyik ágon átnevezzük, másik ágon felüldefiniáljuk felhasználva a régi verziót 52

12.7.2. Polimorfizmus és dinamikus kötés

A metódusok meghívása – A leszármazottban lehetnek új metódusok – C++, Java, Object Pascal, Eiffel a hívható műveleteket a változók statikus típusához köti – Smalltalk, Dylan a metódushívást teljesen dinamikusan kezeli 53

12.7.2. Polimorfizmus és dinamikus kötés C#

class A { public void F() { Console.WriteLine("A.F"); } public virtual void G() { Console.WriteLine("A.G"); } } 54

12.7.2. Polimorfizmus és dinamikus kötés C#

class B: A { new public void F() { Console.WriteLine("B.F"); } public override void G() { Console.WriteLine("B.G"); } } 55

12.7.2. Polimorfizmus és dinamikus kötés

C# class Test { static void Main() { B b = new B(); A a = b; a.F(); b.F(); a.G(); b.G(); } } //A.F B.F B.G B.G 56

12.7.3. Absztrakt osztályok

• Programtervezés: típusspecifikáció kialakítása • Típusértékhalmaz reprezentációja és a műveletek implementációja csak később jön • Absztrakt osztály: nincs döntés a megvalósításról, csak a leszármazottban – tartalmazhatnak absztrakt metódusokat: specifikáltak, de nincs meg az implementációjuk – (és fordítva: ha egy osztálynak van absztrakt metódusa, akkor az az osztály is absztrakt) – Absztrakt osztályból nem lehet példányt létrehozni – Absztrakt osztály típusú változók a polimorfizmus segítségével, valamely leszármazott osztály referenciáit tartalmazhatják 57

12.7.3. Absztrakt osztályok

• Java, C#, Eiffel: egy osztály absztrakt metódus nélkül is lehet absztrakt • C++: szükséges absztrakt metódus • További származtatás megtiltása: Java – final, C# – sealed (lepecsételt), Eiffel – frozen, ezek absztrakt osztályok esetén nem használhatók • Lehetséges absztrakt osztályokból hierarchiát felépíteni – absztraktak maradnak, amíg marad legalább egy absztrakt metódusuk 58

12.7.3. Absztrakt osztályok

Nyelv SIMULA 67 Smalltalk C++ Object Pascal Java C# Eiffel virtuálisnak nem kötelező az implementáció nincs absztrakt kulcsszó nincs, metódusban a jelölés a törzs helyére írt =0 ekkor az osztály is automatikusan absztrakt lesz műveletekre: virtual + abstract az osztály marad példányosítható, legfeljebb kivételt dob, megvalósításnál override abstract – osztályokra és műveletekre is abstract osztályokra és műveletekre is, megvalósításnál override deferred (késleltetett) – osztályokra és műveletekre is, megvalósításnál nem kell külön jelölni undefine – a már megvalósított művelet implementációját törli, szignatúráját meghagyja, absztrakttá teszi (a többszörös öröklés miatt van rá szükség) Ada 95 59

12.7.4. Közös ős

• Az összes osztály egy közös őstől származik: Smalltalk, Java, C#, Eiffel – Közös ősosztály neve általában: Object, TObject – Implicit ős: saját új osztály készítésekor, ha mást nem adunk meg, ez lesz az ős • Object Pascal: object-tel jelölt osztályoknak nincs közös ősük class sal jelölt osztályoknak van, a TObject – Az összes osztályra jellemző műveletek lehetnek itt – Helyettesíthetőség pl.: a metódusok paraméterezésénél (közös ős típusú formális paraméter) • Nincs közös ős: SIMULA 67, C++, Ada 95 – Önálló hierarchia fák alakíthatók ki 60

12.7.5. Többszörös öröklődés

• Többszörös öröklés: egy osztálynak több közvetlen őse van (két, vagy több osztályzásban tekinthető alosztálynak) • Másik megközelítés: a specifikációs és implementációs öröklés vegyítése – egyik ős adja a funkcionalitást, a másik (rejtve) az implementációt 61

12.7.5. Többszörös öröklődés

• megvalósítás C++-ban: – specifikációs öröklés • több publikus ős – specifikációs és implementációs öröklés • specifikáció öröklés nyilvános őstől • implementációs öröklés private, vagy protected őstől • (az alapértelmezett a rejtett öröklés) • Polimorfizmus és dinamikus kötés csak a nyilvános őstől van • A rejtett ősnek nem altípusa a leszármazott osztály – többszörös implementációs öröklés • felüldefiniálással, vagy explicit módon nyilvánossá tehető az örökölt védett, vagy publikus metódus • Eiffel: alapértelmezett a specifikációs öröklődés, de a szelektív láthatóság miatt az implementációs öröklés is támogatott.

62

12.7.5. Többszörös öröklődés

class Animal { public: virtual void eat(); }; class Mammal : public Animal { public: virtual Color getHairColor(); …}; class WingedAnimal : public Animal { public: virtual void flap(); …}; // A bat is a winged mammal class Bat : public Mammal, public WingedAnimal { …}; Bat bat; 63

12.7.5. Többszörös öröklődés

A névütközés kezelése • azonos nevű és szignatúrájú metódusok a különböző ősökben – átnevezés – különbözőek maradnak – összekapcsolás – mindkét ősnek megfelelő törzs (azonos név és szignatúra esetén) 64

12.7.5. Többszörös öröklődés

Átnevezés • Eiffel: öröklési záradékban átnevezhetők az örökölt jellemzők • a nevek túlterhelése nem megengedett: azonos nevű, de különböző szignatúrájú metódusoknál is szükséges az átnevezés • C++-ban nincs átnevezés 65

12.7.5. Többszörös öröklődés

• • • • • • • • Összekapcsolás C++: a többszörösen örökölt felüldefiniált metódust bármelyik ősének referenciáján keresztül eléri ha választunk a két örökölt közül, akkor nem lesz megbízható az eredmény Ha azonos nevű, de különböző szignatúrájú metódusokat örököl egy osztály, akkor a using kulcsszó segít a megfelelő kiválasztásában, a leszármazottban túlterhelt metódusok lesznek Eiffel: összekapcsolás csak azonos nevű és szignatúrájú absztrakt metódusok esetén lehetséges az undefine záradékkal újra absztrakttá tehetünk egy már megvalósított jellemzőt is.

Azonos szignatúrát, vagy nevet átnevezéssel is elérhetünk A létrejövő új metódus előfeltételében az összekapcsoltak előfeltételei

vagy

kapcsolatban, utófeltételében az utófeltételek

és

kapcsolatban egyesülnek.

A leszármazott maradhat absztrakt is, de adhat implementációt is a metódusnak 66

12.7.5. Többszörös öröklődés

A „rombusz-öröklődés” kezelése A B C D Hány példányban kapja meg (D) az örökölt tagokat?

67

12.7.5. Többszörös öröklődés

Adattagok többszörös öröklése • C++: minden adattag annyiszor jelenik meg ahányszor örökölte, és az osztály nevével kell minősíteni. Ha nincs szükség többszörös megadásra, akkor B és C dönthetnek virtuális ős lehetősége mellett is.

• Eiffel: a közös őstől örökölt jellemzők csak egyszer jelennek meg, de átnevezéssel lehet dönteni a többszörözésükről 68

12.7.5. Többszörös öröklődés

Metódusok többszörös öröklése • C++, Eiffel: a közös őstől örökölt virtuális metódusok egyszer jelennek meg • Ha valamelyik osztály (B, C) átdefiniálja, akkor a közös gyerek mindegyiket örökli, amit a C++-ban átdefiniálással, Eiffelben átdefiniálással, vagy átnevezéssel lehet feloldani.

• Ha mindkét közvetlen ős átdefiniál egy közös őstől származó metódust, akkor a polimorfizmus, és a dinamikus kötés miatt probléma van a több létező implementációval: Eiffel bevezeti a select záradékot ennek megoldására 69

12.7.5. Többszörös öröklődés

• A problémákra hivatkozva több nyelv nem engedi a többszörös öröklést • Javaslat a megoldásra: a specifikációs öröklésnél absztrakt osztályoktól örököljünk, a reprezentációhoz szükséges adattagokat és metódusimplementációkat pedig legfeljebb egy őstől kapjuk.

• Ez volt a kiindulópontja az interfészek bevezetésének.

70

12.7.6. Interfészek

• Interfész – speciális absztrakt osztály – Nincsenek példányváltozók, csak metódusok • Használata a megvalósításán keresztül történik • Az interfész helyén bárhol szerepelhet egy azt megvalósító osztály (polimorfizmus) • Az interfészek is öröklési hierarchiába szervezhetők • Interfészek között lehetséges többszörös öröklés is • Egy osztály akárhány interfészt implementálhat • Protokollok az Objective-C-ben 71

12.7.6. Interfészek

Nyelv Objective-C Java Object Pascal C# Egyszeres öröklés Az IUnknown interfész leszármazottai Osztályok vagy rekordok valósíthatják meg protocol: absztrakt műveletek gyűjteménye protokollok között is van többszörös öröklés interface: absztrakt metódusok és konstansok az interface absztrakt, de csak csomagszintű láthatóságú Az interfészek között is van öröklés, többszörös is Absztrakt metódusok és tulajdonságok egy osztály több interfészt is megvalósíthat interface: absztrakt metódusok Interfészek között többszörös öröklés 72

12.7.7 Beágyazott osztályok, tagosztályok

• Osztályok más osztályokon belül (nem csak a legfelső szinten) • Elérése csak a tartalmazó osztályból lehetséges • (Java-ban akár név nélküli belső osztály is lehetséges) 73

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok

• ADA 2005

– változások ADA 95-höz képest • metódus meghívása (Y egy P csomagbeli osztály objektuma) – P.Op(Y,...); helyett Y.Op(...); – többszörös öröklés bevezetése • Java-hoz hasonló, interfészekkel • interface új foglalt szó

package type

P1

is

Int1

is Interface

;

procedure procedure

Op1(X: Int1) N(X: Int1)

is abstract is null

; ;

end

P1; 74

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok

• ADA 2005

– számos típusból lehet egyszerre származtatni, azonban ezek közül csak egyetlen egy lehet normál jelölt típus (amelynek elöl kell állnia) – Az elsőre mint szülőre hivatkozhatunk (mely egy jelölt típus is és felület típus is lehet), bármely másra pedig mint ősre (melyek csak felületek lehetnek). 75

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok

• ADA 2005

type

DT1

is new record

; T1 and Int1

with null type

DT2

is new record

... Int1 and Int2

end record

;

with type with

DT3

is new

...; T1 and Int1 and

Int2

76

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok

• ADA 2005 Új kulcsszó, felülírás explicit jelölése:

overriding procedure Finalize (Obj: in out T); not overriding procedure Op(Obj: in out NT; Data: in Integer);

77

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok • C++ • A Managed Extensions for C++ a C++ szabvány kiterjesztéseinek és módosításainak egy halmaza, amelyet a Microsoft azért alkotott meg, hogy a C++ t kompatibilissé tegye a .NET környezettel. A Managed C++ nem önálló programozási nyelv. A C++/CLI a Managed C++ utóda, több új nyelvi elemet bevezet annak érdekében, hogy a C++ teljes értékű .NET programozási nyelvvé válhasson. 78

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok •

Managed C++

• A felügyelt osztályok viselkedése változott a leginkább a C++/CLI-ben a Managed C++-hoz képest. A Managed C++-ban a * operátorral deklarálhatunk referenciákat egy osztályra, és a __gc new operátorral hozhatjuk őket létre a managed heapen. Az objektumokat a szemétgyűjtő kezeli és semmisíti meg. Ha az osztálynak van explicit destruktora, az objektum delete operátorral manuálisan is megsemmisíthető, ez azonban erősen ellenjavallott. 79

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok •

A C++/CLI ben megváltozott a destruktorok szerepe:

• A ~MyClass() módon deklarált destruktor valójában az IDisposable interfész Dispose metódusát implementálja. Ez a destruktor determinisztikus, azonnal végrehajtódik, ahogy az objektum látótéren kívül kerül. • A !MyClass() metódus lesz a "valódi" destruktor, mely az Object osztály Finalize() metódusát implementálja. Ez a destruktor nem determinisztikus, akkor hajtódik végre, mikor a szemétgyűjtő megsemmisíti az objektumot. 80

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok • A felügyelt osztályok főbb tulajdonságai: Főbb jellegzetességek Lehet olyan adattagja ami felügyeletlen típusú objektumra mutató pointer. Lehet felhasználó által definiált destruktora. Megvalósíthat bármennyi felügyelt interfészt (

interface

). Lehetnek tulajdonságai (

property

). Megjelölhetjük az

abstract

kulcsszóval. Az osztályt zárolhatjuk (

sealed

). Lehet statikus konstruktora. Lehet konstruktora. Lehetnek láthatósági szabályzói (

public

,

protected

,

private

).

Főbb megszorítások Nem származtatható felügyeletlen osztályból. Felügyeletlen osztály nem származtatható belőle. Legfeljebb egy(!) felügyelt osztályból származtatható. Nem lehet felhasználó által írt copy konstruktora. Nem deklarálhat vagy definiálhat friend osztályokat vagy függvényeket. Nem deklarálhat vagy definiálhat new vagy delete operátort. Nem tartalmazhat using deklarációt. Nem lehet konstans (

const

) tagfüggvénye. Ha nem adjuk meg az ősosztályát, akkor a rendszer úgy veszi, hogy a

System::Object

ból származik.

81

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok • Láthatósági szabályok: Név private public Jelentés Csak az adott assemblyben elérhető public láthatósággal. protected public Az adott assembly ben public láthatósági szinten elérhető, az assembly-n kívül csak a származtatott osztályok érhetik el. 82

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok

Zárolt (sealed) osztályok.

• Ezzel megakadályozhatjuk, hogy további osztályok legyenek a zárolt osztályból származtatva. Taggfüggvényeket is elláthatunk ezzel a kulcsszóval, amely nem engedi, hogy felüldefiniáljuk azt a származtatott osztályban. Csak felügyelt osztályoknál lehet alkalmazni. Interfészekhez nem használható.

83

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok

Absztrakt (abstract) osztályok.

• Így olyan osztályt definiálhatunk, amelyből objekumot csak további osztályok származtatásával lehet létrehozni. Természetesen nem használható zárolt osztályokhoz, és érték típusú osztályokhoz sem.

84

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok •

Felügyelt interfészek.

• Olyanok mint az osztályok, csak metódusai teljesen virtuálisak, nem tartalmazhatnak semmilyen megvalósítást. Az amely érték típusú (

__value enum interface

kulcsszóval definiáljuk az ilyen osztályokat. Lehet bennük felsorolási típus, ). Nem kell használni a

virtual

kulcsszót vagy a = 0 szuffixumot.

• Főbb megszorításai: – Nem lehetnek adattagjai, illetve semmilyen fajta statikus tag. – Nem lehetnek semmilyen felügyelt vagy felügyeletlen osztály deklarációi. – Minden csak publikus lehet (alapértelmezett). – Nem lehet őket zárolni. – Nem lehet beágyazni őket felügyelt vagy érték típusú osztályokba. 85

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok • C# •

Szétválasztott osztályok

• Ez a funkció lehetőséget ad arra, hogy egy osztály teljes definícióját fizikailag is különálló forrásfájlokban tároljuk. Ebből a célból bevezettek egy új kulcsszót, a projektben kell lennie és minden formális tapasztalunk.

generált és a kézzel írt kód elkülönítésére.

partial

követelménynek meg kell felelnie. Ekkor a fordító osztállyá, így futási időben semmi különbséget nem • A .NET keretrendszer is használja ezt a megoldást a -t. Egy szétválasztott osztály minden részének tartalmaznia kell a partial kulcsszót. Az osztály összes részének egy felkutatja az osztály összes részét, majd összevágja egy 86

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok • //file1.cs

namespace Clock { //saját változók és metódusok } class Clock { } ...

• //file2.cs Általában "Clock.Designer.cs" namespace Clock { partial class Clock { } ... //generált kód } 87

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok • C# •

A környezet (context) fogalma

• A C#-ban megjelent egy új módosító kulcsszó, az unsafe. Az unsafe kifejezi, hogy az entitás, melyre alkalmazzuk, olyan nyelvi elemet tartalmaz, amely nem "biztonságos". Az entitás lehet blokk, típus, metódus stb. A kulcsszó hatásköre a mindenkori entitás hatáskörével egyezik meg. A nem biztonságos programozási elemek az alábbiak: mutatók (akár void*), a veremfoglalás, tehát a dinamikus memóriakezelés.

88

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok

• C# unsafe class Class1 {} static unsafe void FastMove ( int* pi, int* pdi, int length) {...}

89

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok • •

C# 3.0

• A C# 3.0 a nyelv következő verziója. A változások nagy részét olyan funkcionális nyelvek inspirálták, mint a Haskell, az ML, és a Language Integrated Query minta bevezetése a Common Language Runtime-ba.

Objektum orientáltság

: Customer c = new Customer(); c.Name="James"; – írható ezentúl így is (szintaktikai cukor): Customer c = new Customer{ Name="James" }; 90

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok • Java • Annotációk (Java 5) • Módosítószavak bővítése • A programszöveg elemeihez rendelhetők – Csomagokhoz, típusokhoz, metódusokhoz, attribútumokhoz, konstruktorokhoz, lokális változókhoz • Plusz információt hordoznak – A Java fordító számára – Speciális eszközök számára 91

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok

• Java • Predefinit annotációk • A Java nyelvbe/fordítóba beépítettek

– Target – Retention – Inherited – Override – SuppressWarnings – Deprecated 92

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok

• • Java

@Override @Override

int overriddenMethod() { }

93

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok • • • • • • PHP

Osztályok - PHP4

Bár sok minden hiányzik a teljes objektum-orientáltsághoz (pl. láthatóság), lehetőségünk van öröklődéssel újrahasznosítani kódunkat. Mivel típusok nem léteznek, ezért nem kell foglalkoznunk polimorfizmussal, ami nagyban egyszerűsíti a helyzetet.

Az osztály attribútumait a var kulcsszó jelöli. Bár destruktorok nincsenek, lehetőség van az osztálynak konstruktorokat definiálni, különböző paraméterekkel. Ha egy attribútum nem konstans, hanem függvény értékeként töltődik fel, akkor azt konstruktorban kell inicializálni. Osztályszintű (statikus) attribútumok nincsenek, csak objektumokhoz kapcsolódhatnak.

Konstruktor az a metódus, melynek neve megegyezik azon osztály nevével, amelyben definiálták. Ha egy leszármazott osztálynak nincs konstruktora, a példányosításnál automatikusan meghívódik a szülő osztályának konstruktora (ha van). (PHP3 ban ez nem így működik, ott nem keresi a szülő osztály konstruktorát.) Objektumot a new utasítással tudunk létrehozni. A metódusokban használhatjuk a $this változót, amely az objektumpéldányra mutat. Olyan metódust, amely nem használja az osztály attribútumait és $this-t, hívhatunk objektum nélkül is a :: operátorral (lásd pl. C++).

94

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok • PHP class Kosar { var $dolgok; // A kosárban levo dolgok function berak ($sorsz, $db) { $this->dolgok[$sorsz] += $db; } function id () { echo "kosár vagyok"; } } Kosar::id(); $kosar = new Kosar; • Destruktor: mivel nincsen alapértelmezett destruktor, ezért nekünk kell létrehoznunk azt (ha szeretnénk, hogy legyen.) Péda egy automatizált destruktorra: function destruktor($object) { eval ("global \$" . $object . ";"); ${$object}->destruktor(); unset($GLOBALS[$object]); } 95

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok

• PHP • Az extends kulcsszóval az öröklődést jelölhetjük, többszörös öröklésre nincs lehetőség. A szülőre hivatkozáskor használhatjuk a parent kulcsszót, de használhatunk explicit típusmegjelölést is a :: operátorral.

96

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok • PHP class GazdasKosar extends Kosar { var $tulaj; } function tulajdonosa ($nev) { $this->tulaj = $nev; } function id () { echo "gazdás "; parent::id(); // ugyanaz, mint Kosar::id(); } 97

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok • • PHP

A PHP5 új objektum modellje

• A Zend Enigne 1.0 objektum modelljében, a pédányosított objektumok nyelvi szintű elemek. Ez azt jelenti, hogy amikor a programozók műveleteket hajtanak végre az objektumokkal, akkor a nyelv az egyszerű típusokhoz nagyon hasonlóan kezeli az objektumokat. Szemantikusan ez azt jelenti, hogy ilyenkor az egész objektum lemásolódik. A Java megközelítése teljesen más hiszen az objektumokat egy 'handle n', azaz kezelőn keresztül éri el, ezt úgy képzelhetjük el, mint az objektumoknak kiosztott egyedi azonosító.

98

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok • • PHP

Az új modell leegyszerűsítve

• A létrehozott objektum modellt nagyban befolyásolta a Java objektum modellje. Általánosan, ha egy új objektumot példányosítunk, akkor az objektumhoz egy handle t kapunk ahelyett, hogy önmagát az objektumot kapnánk meg. Amikor ezt a handle-t függvény bemenő paraméterként adjuk át, illetve más egyéb esetekben is, csak és kizárólag a handle az, ami átatódik az objektum maga soha! Az objektum maga soha nem másolódik le, és nem is duplikálódik, aminek eredményeként egy objektum minden handle-je mindig ugyanarra az objektumra mutat. • $object = new MyClass(); $object->method(); 99

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok • Ruby •

Áttekintés

• A Ruby egy objektumorientált programozási nyelv, azaz programjainak alapköve az osztály. A Ruby osztálykezelése sok szempontból eltér más objektumorientált nyelvektől. Számos olyan dolog hiányzik belőle, ami megvan a legtöbb nyelvben, azonban interpretált nyelv lévén olyan lehetőségeket is magában rejt, amelyek egy natív kódra fordító nyelvben (mint amilyen a C++), elképzelhetetlenek.

100

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok • • Ruby

Áttekintés

• A Ruby teljesen objektumorientált. Minden függvény egy osztály tagfüggvénye, még azok is, amelyeket látszólag osztályon kívül definiáltunk. Az osztályok olyan hierarchiába szerveződnek, amelynek a gyökere az

Object

osztály. Ez minden osztálynak őse. Minden függvény virtuális, tehát hiányoznak a nem virtuális tagfüggvények. Hiányzik a többszörös öröklődés, a konstans függvények, a tisztán virtuális függvények és szigorú értelemben a hozzáférhetőség sem szabályozható. További sajátosság, hogy a nyelvben minden objektum, még az osztályok is! 101

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok • • Ruby

Áttekintés

• Úgynevezett mixinek segítségével lehetőség van "visszafelé származtatásra", azaz előre megírhatunk egy leszármazást, amit tetszőleges ősosztályra alkalmazhatunk. A legérdekesebb tulajdonság azonban a reflexivitás: egy Ruby program "tud saját magáról", azaz ismeri a saját kódját, így azt futási időben meg is tudja változtatni. Lehetőség van osztályokhoz futási időben újabb függvényeket hozzávenni, sőt objektumszinten is lehet származtatni, azaz futási időben hozzá lehet adni egyetlen objektumhoz egy függvényt. 102

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok • • • VB.NET • • •

Újdonságok

• A legfőbb változás, hogy a VB.NET teljesíti az objektum orientált fejlesztőrendszer mind a négy szükséges feltételét:

Absztrakció

(Abstraction): a megoldandó feladat modellezése objektumok segítségével.

Egységbe zárás

(Encapsulation): egy objektum belső megvalósítása kívülről nem elérhető.

Többalakúság

(Polymorphism): több objektum ugyanazon tulajdonsága (metódusa stb.) más lehet.

Öröklés

(Inheritance): a származtatott objektumok öröklik az elődobjektum tulajdonságait (metódusait stb.).

103

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok

• VB.NET

• VB.NET–ben minden változó objektum, még egy egyszerű egész is. Minden objektum a System.Object nevű alapobjektumból származik, örökölve ennek alaptulajdonságait (metódusait stb.). Így például — bár minden objektumnak kell legyen létrehozó (constructor), illetve felszabadító (destructor) metódusa — ezeket nem szükséges megírni saját objektumainkhoz, ha megelégszünk a System.Object alapmetódusaival. 104

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok • VB.NET •

Konstruktorok és destruktorok:

• A VB.NET-ben konstruktor és destruktor váltja az osztályok deklarációja

Initialize

és

Terminate

eseményeit. A működésük lényegében azonos, egy fontos kivétellel, hogy a konstruktort és a destruktort nem lehet explicit hívni, és így biztosan csak egyszer futnak le. A konstruktor

Sub New...

, a destruktoré

Sub Destruct...

A program default konstruktort csak akkor hoz létre, ha mi nem hoztunk létre sajátot.

105

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok • VB.NET • Alapértelmezésben a konstruktorok nem öröklődnek Visual Basicben. Ha az utódosztály konstruktorából meghívjuk az ősosztály konstruktorát, akkor azt még az első sorban kell megtenni. hatókörét vagy

Finalize Nothing

-

Finalize

metódus (destruktor) automatikusan hívódik meg, ha egy objektum elhagyja ra állítjuk. Ezzel kikényszeríthetjük, hogy a hulladékgyűjtő a következő alkalommal eltávolítsa az objektumot a memóriából. metódusnál használnunk kell az

Overrides

kulcsszót, mert tulajdonképpen az beépített

Finalize Object

osztályba metódust írjuk felül, mivel minden osztály, még ha nem is jelöltük, ennek az Object nevezetű ősosztálynak a leszármazottja. 106

Kiegészítések, a könyvben nem szereplő nyelvek, újdonságok • VB.NET •

Public Class End Class

Student

Inherits

Person ...

Public NotInheritable Class End Class

Person ...

Public MustInherit Class End Class

Person ...

Class

Ember

Private Shared End Class

nepesseg

As

Integer ...

107

Könyv hibajegyzék

• Kiskapu kft., Budapest, 2003.

– 403. oldal: Valt = new AOszt(); AOszt valt = new AOszt(); – 427. oldal: „programozás nyelv”, helyett programozási nyelv – 435. oldal: „Ösosztály” helyett, Ősosztály – 446. oldal: utolsó sor „struktúrákat listáit”, helyett struktúrák listáit 108

Köszönöm a figyelmet!

[email protected]

109

Viccek

• Chuck Norris bármilyen nyelven tud több közvetlen őstől örököltetni.

• Chuck Norris tud olyan osztályt írni, ami egyszerre abstract és final.

• A garbage collector meg se próbálja Chuck Norris objektumait elpusztítani. Elpusztíthatatlanok.

• Chuck Norris a 'Isten' osztályból származik, és csak ő valósítja meg az 'IElpusztíthatatlan' interface-t. Az összes többi osztály a 'ChuckNorrisLerúghatja' osztályból származik.

• Felesleges private módosítót használnunk, mert Chuck Norris előtt semmi sem lehet rejtve.

• SOHA, ne próbáljunk hozzáférni egy olyan taghoz, ami a 'protected by Chuck Norris' módosítóval van ellátva.

Sajnos nem tudjuk elmondani, hogy miért, mert nincsen túlélő.

110