Előadást letölteni
Az előadás letöltése folymat van. Kérjük, várjon
KiadtaAndor Boros Megváltozta több, mint 10 éve
1
1 Hernyák Zoltán Programozási Nyelvek II. Eszterházy Károly Főiskola Számítástudományi tsz
2
2 Az osztályok megírásánál szempont, hogy az osztály felelős saját maga helyes működéséért. Ennek érdekében … - induláskor egy alap állapotba kell helyezze magát, majd … - működés közben nem szabad engednie, hogy hibás állapotban helyezhessék. E miatt elrejti a kritikus mezőit a külvilág elől, és a publikus metódusai pedig csak ellenőrzött módon változtatják meg az objektum állapotát.
3
3 A CONSTRUCTOR olyan speciális metódus, amelyet a példány létrehozásakor kell meghívni. Ezen metódus felelős azért, hogy az objektum-példányt alap helyzetbe álljon. Ha a programozó elfelejti meghívni a konstruktort példányosításkor, akkor a példány nincs alaphelyzet- ben, használata során futás közbeni hibák, hibás működés következhet be. ‘Tisztességes’ programozási nyelvben nem lehet megkerülni a konstruktor hívását a nyelv szabályai miatt. A C# egy ‘tisztességes’ programozási nyelv.
4
4 A C#-ban a CONSTRUCTOR: kötött a neve (uaz, mint az osztály neve) nincs visszatérési típusa fő feladata az osztály alaphelyzetbe állítása végrehajtódik, mielőtt a példány példány szintű metódusait meghívni esélyünk lenne végrehajtódik, mielőtt tetszőleges virtuális metódust meghívni esélyünk lenne
5
class TVerem { private int vm; public double Pop() {... } public void Push() {... } } class TVerem { private int vm; public double Pop() {... } public void Push() {... } } public TVerem () { vm = 0; } public TVerem () { vm = 0; } 5 TVerem v = new TVerem(); Konstruktor hívása ! A példány számára memória foglalása !
6
6 A példányosítás két lépésből áll: -memória foglalás a mezők számára -A konstruktor meghívása Ezen két lépés nem szétválasztható! TVerem v; … v = new TVerem(); v.Push( 100 ); TVerem v; … v = new TVerem(); v.Push( 100 ); TVerem v = new TVerem(); v.Push (100 ); TVerem v = new TVerem(); v.Push (100 ); A konstruktor lefut mielőtt meghívhatnánk bármely példányszintű metódust!
7
7 A fenti kód írása lehetséges, de hibás. Nem történt meg a példányosítás, mielőtt a metódust meghívtuk volna! Ezért nem a konstruktor a felelős! Az ilyen programozási hibákat a C# fordító egyébként is észreveszi, és hibát jelez: TVerem v; v.Push( 100 ); TVerem v; v.Push( 100 ); Use of unassigned variable v! Use of = ‘használata’ Unassigned variable = olyan változó, amely még nem kapott értéket.
8
8 A C#-ban a CONSTRUCTOR: … „kötött a neve (uaz, mint az osztály neve)” … Azt gondolnánk, hogy 1 osztály -> 1 konstruktor. De nem! Az overloading-nak köszönhetően -tetszőleges sok constructor-t készíthetünk az osztályokhoz… -csak mindegyiknek el kell térni egymástól a paraméterezésben (más-más paraméterezéssel)
9
class TKor { private int X,Y; public TKor() { X = 0; Y = 0; } public TKor(int kezdoX, int kezdoY) { X = kezdoX; Y = kezdoY; }... } class TKor { private int X,Y; public TKor() { X = 0; Y = 0; } public TKor(int kezdoX, int kezdoY) { X = kezdoX; Y = kezdoY; }... } 9 TKor k1 = new TKor(); TKor k2 = new TKor(10,20); TKor k1 = new TKor(); TKor k2 = new TKor(10,20);
10
class TAudi { private int motorLE = 160; private double fogyaszta = 14.5; private int maxSebesseg = 240; public void Inditas() {...} public void BalraFordul(int szog) {...}... } class TAudi { private int motorLE = 160; private double fogyaszta = 14.5; private int maxSebesseg = 240; public void Inditas() {...} public void BalraFordul(int szog) {...}... } 10 TAudi a = new TAudi(); Ez is konstruktor-hívás! Ha nem írunk egy osztályhoz konstruktort, akkor egy alapértelmezett konstruktor generálódik az osztályhoz!
11
11 A C alapú nyelvekben (C, C++, Java, C#, …) létezik a ‘kezdőértékadás’ fogalom. Ha a konstuktorban csak konstans kezdőértékeket akarnánk a mezőkhöz rendelni, akkor ehhez felesleges konstruktort írni. Ezt megtehetjük kezdőértékadás formájában is. Ez eléggé gyakori eset. Ezért konstruktort ténylegesen csak ‘ritkán’ írunk. Viszont a példányosítás során kötelező meghívni egy létező konstruktort!
12
12 Ha nem írunk egy konstruktort sem: akkor a fordító- program generál 1 üres paraméterezésű, üres törzsű publikus konstruktort. Ha mi írunk legalább 1 db konstruktort, akkor viszont a fordítóprogram nem fog készíteni ilyet. class TKor { private int X,Y; public TKor(int kezdoX, int kezdoY) { X = kezdoX; Y = kezdoY; } class TKor { private int X,Y; public TKor(int kezdoX, int kezdoY) { X = kezdoX; Y = kezdoY; } TKor k = new TKor(); Ez hibás, nincs ilyen paraméterezésű konstruktor!
13
13 A konstruktorok védelmi szintje általában public. Ha nem public, akkor ugyanis nem lehet meghívni őket! class TKor { private int X,Y; private TKor(int kezdoX, int kezdoY) { X = kezdoX; Y = kezdoY; } class TKor { private int X,Y; private TKor(int kezdoX, int kezdoY) { X = kezdoX; Y = kezdoY; } TKor k = new TKor(10,20); Ez hibás! Van ilyen paraméterezésű konstruktor, de ‘kívülről’ nem elérhető!
14
14 Az alábbi példa más szempontból is érdekes: Ebből az osztályból fizikai képtelenség példányt készíteni! Mivel példányosításkor kötelező konstruktort hívni, de ezen osztály konstruktorát nem lehet meghívni kívülről, ezért itt bezárult a kör! class TKor { private int X,Y; private TKor(int kezdoX, int kezdoY) { X = kezdoX; Y = kezdoY; } class TKor { private int X,Y; private TKor(int kezdoX, int kezdoY) { X = kezdoX; Y = kezdoY; } TKor k = new TKor(10,20);
15
15 Ez lehet cél is! Hogy megakadályozzuk a példányosítást. Ez főleg akkor jön elő, ha az osztályunknak ‘csak’ osztályszintű metódusai vannak (pl. Console, Math, …) class Console { private Console() { } class Console { private Console() { } Console c = new Console(); Azért kell megírni a konstrukort kézzel, hogy a fordító ne hozzon létre helyettünk konstruktort, mert amit ő csinál, az public lenne!
16
16 Más értelme is van a konstruktor elrejtésének: class TSajat { private TSajat() { } public static TSajat Letrehoz() { return new TSajat(); } class TSajat { private TSajat() { } public static TSajat Letrehoz() { return new TSajat(); } TSajat s = new TSajat(); TSajat s = TSajat.Letrehoz(); Ez nem megy! De ez működik! Itt szabad hívni private szintű metódusokat!
17
17 Az ilyen jellegű megoldásokat objektum-gyárnak (object factory) nevezzük. Az ilyen metódusoknak osztály szintűnek (static) kell lenniük, hogy példányosítás nélkül is meghívható legyen! Az object factory különböző ellenőrzéseket végezhet el, mielőtt új példányt hoz létre, illetve meg is tagadhatja a példány létrehozását! Ilyet használunk, ha értesülni akarunk a példányosításról! public static TSajat Letrehoz() { return new TSajat(); } public static TSajat Letrehoz() { return new TSajat(); }
18
18 public static TSajat Letrehoz(string nev, string jelszo) { if ( JelszoStimmel(nev,jelsz) ) return new TSajat(); else return null; } public static TSajat Letrehoz(string nev, string jelszo) { if ( JelszoStimmel(nev,jelsz) ) return new TSajat(); else return null; } TSajat s = TSajat.Letrehoz(”pisti”,”jelszo123”); if (s==null) { … nem jó a jelszóm … } else { … dolgozom a példánnyal … } TSajat s = TSajat.Letrehoz(”pisti”,”jelszo123”); if (s==null) { … nem jó a jelszóm … } else { … dolgozom a példánnyal … }
19
19 private static dbSzam = 0; public static TSajat Letrehoz() { if (dbSzam>=10) return null; else { dbSzam++; return new TSajat(); } private static dbSzam = 0; public static TSajat Letrehoz() { if (dbSzam>=10) return null; else { dbSzam++; return new TSajat(); } TSajat s = TSajat.Letrehoz(); if (s==null) { … túl sok létrehozott példány … } else { … dolgozom a példánnyal … } TSajat s = TSajat.Letrehoz(); if (s==null) { … túl sok létrehozott példány … } else { … dolgozom a példánnyal … }
20
20 A konstruktorok minden más szempontból egyszerű metódusnak tekintendőek. Szabad belőlük másik metódust is meghívni! class TKor { private int X=10,Y=10; private int sugar = 10; public TKor() { Kirajzol(); } public void Kirajzol() {... } class TKor { private int X=10,Y=10; private int sugar = 10; public TKor() { Kirajzol(); } public void Kirajzol() {... }
21
21 De mi a helyzet a virtuális metódusokkal? class TAlapGrafikusOsztaly { public virtual Kirajzol() {... } } class TKor:TAlapGrafikusOsztaly { private int X=10,Y=10; private int sugar = 10; public TKor() { Kirajzol(); } public override Kirajzol() {... } class TAlapGrafikusOsztaly { public virtual Kirajzol() {... } } class TKor:TAlapGrafikusOsztaly { private int X=10,Y=10; private int sugar = 10; public TKor() { Kirajzol(); } public override Kirajzol() {... }
22
22 A virtuális metódusok helyes kezeléséhez VMT tábla megléte szükséges. A VMT táblát a példány készítésekor a példányhoz kell hozzárendelni (csatolni). A VMT táblát éppen a konstruktor rendeli hozzá a példányhoz public TKor() { Kirajzol(); // ezért itt már működik is ! } public override Kirajzol() {... } public TKor() { Kirajzol(); // ezért itt már működik is ! } public override Kirajzol() {... } // itt történik meg a VMT hozzárendelés (automatikus)
23
23 A konstruktor ha más semmit sem csinál, akkor is megtesz annyit, hogy a VMT táblát hozzárendeli a példányhoz. Ez a hozzárendelés a legtöbb nyelven a konstruktor kódjának futása előtt történik meg. Ezért a konstruktor belsejében már működik a késői kötés, és lehet hívni a virtuális metódusokat! public TKor() { Kirajzol(); // ezért itt már működik is ! } public override Kirajzol() {... } public TKor() { Kirajzol(); // ezért itt már működik is ! } public override Kirajzol() {... } // itt történik meg a VMT hozzárendelés (automatikus)
24
24 A konstruktor ha más semmit sem csinál, akkor is megtesz annyit, hogy a VMT táblát hozzárendeli a példányhoz. Ez a hozzárendelés a legtöbb nyelven a konstruktor kódjának futása előtt történik meg. Ezért a konstruktor belsejében már működik a késői kötés, és lehet hívni a virtuális metódusokat! public TKor() { Kirajzol(); // ezért itt már működik is ! } public override Kirajzol() {... } public TKor() { Kirajzol(); // ezért itt már működik is ! } public override Kirajzol() {... } // itt történik meg a VMT hozzárendelés (automatikus)
25
Konstruktorból meghívható egy másik konstruktor is, speciális módon ( ‘:’ és a konstruktor neve) class TKor { private int X,Y; public TKor():TKor(0,0) { // itt már más dolgunk nincs is } public Tkor(int ujX, int ujY) { X = ujX; Y = ujY; } class TKor { private int X,Y; public TKor():TKor(0,0) { // itt már más dolgunk nincs is } public Tkor(int ujX, int ujY) { X = ujX; Y = ujY; } TKor k1 = new TKor(30,40); TKor k2 = new TKor(); TKor k1 = new TKor(30,40); TKor k2 = new TKor(); 25
26
Konstruktorból meghívható az ős osztály konstruktora is, speciális módon ( ‘:’ és ‘base’ szó) class TOs { public TOs(int akarmi) { } class TGyerek:TOs { public TGyerek():base (10) {... } class TOs { public TOs(int akarmi) { } class TGyerek:TOs { public TGyerek():base (10) {... } 26
27
27 Ha az osztályunknak van őse, akkor mielőtt a mi konstuktorunk lefutna, előtte a rendszer lefuttatja automatikusan az összes ős osztályunkból legalább egy db konstruktort. Ha az ősnek van paraméter nélküli konstruktora, akkor azt a C# automatikusan kiválasztja és meghívja. Ha ilyen nincsen neki, akkor az ős konstruktort nekünk kell felparaméterezni, és meghívni a ‘base(…)’ hívással!
28
E miatt ha az ősnek csak paraméteres konstruktora van, akkor a gyermek osztályban is kötelező konstuktor írni! class TOs { public TOs(int akarmi) {... } class TOs { public TOs(int akarmi) {... } Ekkor ugyanis (mivel mi nem írunk konstuktort) a fordítóprogram fog egy üres konstuktort generálni, de honnan adjon át paramétert az ősnek? class TGyerek:TOs { // nem írunk konstruktort } class TGyerek:TOs { // nem írunk konstruktort } 28
29
class TOs { public TOs(int akarmi) {... } class TOs { public TOs(int akarmi) {... } Az előző megoldás ekvivalens ezzel: class TGyerek:TOs { public TGyerek() {... } class TGyerek:TOs { public TGyerek() {... } 29
30
class TOs { public TOs(int akarmi) {... } class TOs { public TOs(int akarmi) {... } De az ős konstruktorát ilyenkor nekünk kell meghívni, és felparaméterezni! class TGyerek:TOs { public TGyerek():base(0) {... } class TGyerek:TOs { public TGyerek():base(0) {... } 30
31
class TOs { public TOs(int akarmi) {... } class TOs { public TOs(int akarmi) {... } Gyakoribb, hogy amilyen paramétert az ős bekér, olyat mi is kérünk, és továbbadjuk az ősnek! class TGyerek:TOs { public TGyerek(int akarmi):base(akarmi) {... } class TGyerek:TOs { public TGyerek(int akarmi):base(akarmi) {... } 31
32
Ha egy osztálynak vannak osztályszintű metódusai, és osztályszintű mezői, akkor ezeket is alaphelyzetbe kell állítani! Erre nem jó az eddig tanult konstruktor, mert az a példányszintű alaphelyzetről gondoskodik csak, hiszen példányosításkor fog lefutni. Osztályszintű mezők eléréséhez, metódus hívásához pedig nem is kell példányosítani. Ezért létezik az osztályszintű konstruktor! 32
33
class TSajat { static TSajat() {... } } class TSajat { static TSajat() {... } } Osztályszintű konstruktor: - neve ugyanaz, mint az osztály neve - nem lehet paramétere ! - static módosítójú - kötelezően public (ezért nem is lehet neki semmilyen elérési szint módosítója) Osztályszintű konstruktorból még az overloading mellett sem lehet több, mint 1 db ! 33
34
Az osztályszintű konstruktort kódból nem lehet meghívni (explicit módon)! Az osztályszintű konstruktort a futtató rendszer hívja meg automatikusan (ezért nem lehet neki paramétere), a program indulásának elején! 34
Hasonló előadás
© 2024 SlidePlayer.hu Inc.
All rights reserved.