Miskolci Egyetem Általános Informatikai Tanszák
Osztálytervezés és C++ implementációs ajánlások I. Ficsor Lajos Miskolci Egyetem Általános Informatikai Tanszék Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
1
Osztály tervezés Egy nyelv szabályrendszere számos konstrukciót megenged A software fejlesztés során egy adott problémát kell megoldani Az objektum orientált program a valóság valamely részének a modellje Szükséges tanulmányozni, hogy adott cél elérése érdekében milyen nyelvi szerkezet(ek) használata a legcélszerűbb Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
2
Mi is az osztály? Programozástechnikai szempontból típus. Programtervezési szempontból a modellezendő valóság valamely fogalmának modellje. A programokban kétféle osztályok lehetnek: közvetlenül az alkalmazási terület (application domain) fogalmait ábrázoló osztályok a programok elkészítéséhez szükséges osztályok
Ficsor Lajos
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
3
CBEV/ 1
Miskolci Egyetem Általános Informatikai Tanszák
Mit ábrázolhatnak az osztályok? Alkalmazási terület osztályai: Felhasználói fogalmak (pl. teherauto) Felhasználói fogalmak általánosításai (pl. jármű)
A megvalósítás osztályai: Hardware/software erőforrások (pl. memória, i/o) Más osztályok implementálásához szükséges osztályok (pl. listák, vermek stb.) Beépített adattípusok és vezérlési szerkezetek
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
4
OTERV1 /
5
Osztályok közötti kapcsolatok Logikai szintű kapcsolatok általánosítás/pontosítás (is-a) tartalmazás (has-a) használat (use)
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
Az általánosítás/pontosítás implementálása Leszármaztatási mechanizmus (öröklődés) segítségével public öröklési mód szükséges A leszármazott osztály objektuma egyben ős objektum is.
Ficsor Lajos
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
6
CBEV/ 2
Miskolci Egyetem Általános Informatikai Tanszák
Példa: öröklődés class CSzemely { … } class CHallgato : public CSzemely {…} void SortIszik( const CSzemely& sz ); void FeladatotBead(const CHallgato& h); Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
7
OTERV1 /
8
OTERV1 /
9
Példa: öröklődés (folyt.) CHallgato nagypista; CSzemely kispista; SortIszik( kispista ); // OK // OK, mert egy Hallgato egyben // Szemely is SortIszik( nagypista ); // Hibás, mert egy Szemely nem // biztos, hogy Hallgato is FeladatotBead( kispista ); Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
Példa: hibás öröklődés class CMadar { public: CKaja MitEszik(void); void Repul(void); } class CStrucc: public CMadar {…} A CStrucc örökli a repülés képességét a CMadar osztálytól. Ficsor Lajos
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
CBEV/ 3
Miskolci Egyetem Általános Informatikai Tanszák
Példa: hibás öröklődés (folyt.) A hiba oka: a természetes nyelv pontatlansága. Mit jelent pontosan: "A madár tud repülni" "Minden madár tud repülni" "A madarak általában tudnak repülni" Nem a valóság viszonyait modelleztük.
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
10
Példa: hibás öröklődés javítása Egyik lehetséges javítás: class CMadar { public: CKaja MitEszik(void); bool TudRepulni(void); } class CStrucc : public CMadar {…} Csak a repülés lehetőségét örökli. Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
11
Példa: hibás öröklődés javítása (folyt) class CMadar { public: CKaja MitEszik( void ) ; }; class CGyalogMadar : public CMadar { … }; class CRendesMadar : public CMadar { public : void Repul( void ) ; }; class CStrucc : public CGyalogMadar{ }; class CSas : public CRendesMadar{ }; Ficsor Lajos
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
12
CBEV/ 4
Miskolci Egyetem Általános Informatikai Tanszák
A tartalmazás implementálása Kétféle tartalmazás kapcsolat: agregáció: a rész az egészhez tartozik, de önállóan is létező entitás kompozíció: a rész önmagában nem létezhet, csak valaminek a részeként
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
13
A tartalmazás implementálása: aggregáció Lehetséges megoldások: tag objektum pointere vagy referenciája a tartalmazó osztályban hátrány: szoros kapcsolat a tartalmazó és a tartalmazott objektum között (konzisztencia!!!)
a tartalmazott private leszármaztatása a tartalmazó osztályból hátrány: ilyenkor csak osztály szinten fejezi ki a tartalmazott önálló voltát. Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
14
Példa: aggregáció implementálása (Tag objektum hátrány: implementációs szinten nem fejezi ki a rész önállóságát!)
Példa: class CAuto { Cmotor* m_Motorja ; Ckerek* m_Kerekei[5] ; } Ficsor Lajos
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
15
CBEV/ 5
Miskolci Egyetem Általános Informatikai Tanszák
Tartalmazás implementálása: kompozíció Lehetséges megoldások: tag objektum A tartalmazó osztályban lokális osztálydefiníció a tartalmazott számára
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
16
Használat kapcsolat implementálása Fajtái: (CX és CY osztályok) CX használja a CY nevet (feltételezi, hogy CY deklarált a használat helyén) adattag típusa tagfüggvény visszatérési értékének vagy paramétereinek típusa
CX használja a CY osztályt (láthatósági szabályok alapján) a CY egy tagfüggvényét írja/olvassa a CY egy adattagját Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
17
Használat kapcsolat impl. (folyt.) CX létrehoz egy CY típusú objektumot statikus definíció new operátorral dinamikusan
CX veszi CY méretét Probléma a használat kapcsolat implementálásával: Csak közvetett nyelvi eszközök jelzik az ilyen kapcsolatokat Korlátozó tényezők: azonosítók lexikális érvényességi tartománya osztály tagjainak láthatósági attribútumai Ficsor Lajos
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
18
CBEV/ 6
Miskolci Egyetem Általános Informatikai Tanszák
Információ rejtés A jól tervezett osztály belső részleteinek ismerete szükségtelen a használatához Az osztály használata csak annak interface-e segítségével lehetséges. Egy objektum a külvilággal csak az interface-en keresztül képes kommunikálni.
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
19
Osztály interface fogalma a public tagfüggvények összessége ezt kell ismernie az osztály használójának a leszármazott osztály se tételezzen fel implementációs részleteket a bázisosztályáról
A friend függvények és osztályok csak feltétlenül szükséges esetben használjuk, mert ellentmond az információ rejtés alapelvének
protected függvények és adattagok implementációs függést hoz létre az ős és a leszármazott osztály között Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
20
A jó osztály interface 1. Teljes minden funkciót tartalmaz, ami az osztálytól elvárható nem az adott alkalmazás szempontjai határozzák meg újrafelhasználható egység
Minimális nem tartalmaz a felhasználó számára érdektelen (esetleg veszélyes) elemeket belső felhasználású funkciók private vagy protected minősítésűek a belső áttervezés nincs rá hatással Ficsor Lajos
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
21
CBEV/ 7
Miskolci Egyetem Általános Informatikai Tanszák
A jó osztály interface 2. Kezelhető méretű általában legfeljebb néhány tíz metódus A sok funkció között nagyobb valószínűséggel lesznek hasonlóak (félreértés veszélye!) A terjedelmes interface általában tervezési hibára utal: Az interface része belső funkció is Az osztály határait nem jól állapítottuk meg, és túl sok feladatot akarunk rábízni. A helyes architektúra kialakítása érdekében az eredetileg tervezett osztályt több osztályra kell bontani, és ezek között leszármaztatással vagy más mechanizmussal megteremteni a kapcsolatot.
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
22
Az osztály interface részei Kezelő függvények inicializáló, értékadó, konverziós stb. függvények sokszor nem is a felhasználó program, hanem a fordítóprogram implicite hívja meg.
Elérési függvények Az adattagok értékének elérésére vagy azok értékének módosítására.
Munkavégző függvények Az osztály lényegi funkcióit aktivizáló függvények. Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
23
Kezelő függvények A kezelő függvények kialakítása az osztály használhatóságát alapvetően befolyásolja Egyes kezelő függvények hiánya vagy helytelen implementációja a jól implementált munkavégző függvények ellenére is programhibákhoz vagy hibás működéshez vezethet
Ficsor Lajos
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
24
CBEV/ 8
Miskolci Egyetem Általános Informatikai Tanszák
Kezelő függvények tervezésének szempontjai 1. Hogyan kell az objektumoknak létrejönniük és megszűnniük? konstruktorok száma, paraméterlistájuk a destruktor működése a new és delete operátorok esetleges átdefiniálásának szükségességét.
Miben különbözik egy objektum inicializálása és értékadása? A konstruktorok és az értékadó operátor működése. Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
25
Kezelő függvények tervezésének szempontjai 2. Milyen kezdőállapoto(ka)t vehet fel egy objektum a keletkezése után? valamennyi tagfüggvény implementációját befolyásolja, hogy milyen állapotot tételezhet fel, illetve köteles ellenőrizni.
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
26
Kezelő függvények tervezésének szempontjai 3. Milyen legális állapotokat vehet fel az osztály egy objektuma? A tagfüggvények hibaellenőrzését befolyásolja.
Van bázisosztálya az új osztálynak? Mely funkciók örökölhetők változatlanul? Vannak-e a bázisosztályban olyan függvények, amelyeket felül kell definiálni a leszármazott osztályban? Vannak-e a bázisosztályban olyan függvények, amelyek használatát le kell tiltani? Ficsor Lajos
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
27
CBEV/ 9
Miskolci Egyetem Általános Informatikai Tanszák
Kezelő függvények tervezésének szempontjai 4. Lesz-e várhatóan az osztálynak leszármazott osztálya? Mely tagfüggvények, esetleg adattagok legyenek protected minősítésűek?
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
28
Kezelő függvények tervezésének szempontjai 5. Milyen operátorok definiálása lehet hasznos az osztály használatában? Az operátor overloading tervezéséhez. A felhasználó számára nem ajánlott, de az osztály implementációja számára hasznos operátorok definiálhatók private (esetleg protected) minősítéssel.
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
29
Kezelő függvények tervezésének szempontjai 6. Miknek lehet szüksége a nem public tagok elérésére? Mely adattagokat és tagfüggvényeket célszerű protected minősítéssel ellátni? Mely osztályok illetve függvények kaphatják meg a friend minősítést? Információ rejtésnek ellentmondó konstrukciók => gondosan elemzendő a szükségességük. Különösen érdemes átgondolni, hogy a public adattag használata valóban elkerülhetetlen-e? Ficsor Lajos
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
30
CBEV/ 10
Miskolci Egyetem Általános Informatikai Tanszák
Konstruktor tervezésének szempontjai A felhasználás kényelme Az alapértelmzés szerinti paraméter értékek használata csökkentheti a szükséges konstruktorok számát. A konstruktornak konverziós szerepe is van! Alapértelmezés szerinti konstruktor átdefiniálása szükséges-e? copy konstruktor! Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
31
Destruktor tervezésének szempontjai Minden "mellékhatás" eltüntetése. Bármilyen (legális) állapotban levő objektumra működnie kell!
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
32
Operátorok tervezésének szempontjai 1. Alapszabály: minden olyan operátort definiálni kell, ami az adott osztály használata során természetesnek tűnik. Tervezési hiba, ha egy átértelmezett operátor jelentése a programozó számára nem egyértelmű. Különösen problémás egy, az operátor eredeti jelentésének ellentmondó jelentés definiálása. Különleges szerepe van az értékadó operátoroknak. Ficsor Lajos
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
33
CBEV/ 11
Miskolci Egyetem Általános Informatikai Tanszák
Operátorok tervezésének szempontjai 2. Ha egy relációs operátort értelmezünk, az összeset definiáljuk. Sokszor hasznos lehet (és bármilyen típusú objektumra alkalmazható) az == és a != operátorok implementálása. Nagyon sokszor hívott funkcióra kényelmes megoldás lehet a () (függvényhívás-operátor) átdefiniálása.
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
34
Tagfüggvények implementációja Az eddigi programozói tapasztalatok tanulságai Két alapvető cél: Helytelen működés elkerülése Hatékonyabb működés biztosítása
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
35
OTERV1 /
36
A const minősítő Használjuk mindenütt, ahol lehetséges Szimbolikus konstans definiálására makródefiníció helyett használjunk const minősítésű és inicializált változót (ekkor vonatkozik rá a típusellenőrzés)
Ficsor Lajos
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
CBEV/ 12
Miskolci Egyetem Általános Informatikai Tanszák
A const minősítő (emlékeztető) Konstansra mutató pointer const char* nev = "Ficsór"; A konstans értéke így nem változtatható meg a pointeren keresztül, de a pointer tetszőlegesen újradefiniálható.
Konstans pointer char* const nev = "Ficsór"; A pointer értéke nem változtatható meg (hiszen konstans).
Konstansra mutató konstans pointer const char* const nev = "Ficsor"; Sem a pointer értéke, sem az általa címzett érték nem változtatható meg Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
37
Konstans tagfü tagfüggvé ggvény Minősítése az argumentumlista után tett const alapszóval. Az alapszót mind a függvény prototípusában, mind a definícióban ki kell írni. Jelentése: a konstans tagfüggvény nem változtathatja meg egyetlen adattag értékét sem. Egy ilyen utasítás a függvény törzsében fordítási hibát okoz.
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
38
Konstans tagfüggvények használata 1. Minden olyan tagfüggvényt, amely nem változtatja meg az objektum állapotát, deklaráljunk konstans tagfüggvényként. Konstans objektumra csak konstans tagfüggvény hívható meg. Ha egy tagfüggvényből létezik const minősítésű és minősítetlen változat is, const objektumra const tagfüggvény hívódik meg. Ficsor Lajos
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
39
CBEV/ 13
Miskolci Egyetem Általános Informatikai Tanszák
Konstans tagfüggvények használata 2. Az, hogy egy tagfüggvény definíciója nem tartalmaz olyan utasítást, amely valamely adattag értékét közvetlenül megváltoztatja, még nem jelenti azt, hogy valamilyen közvetettebb módon nem változhat az objektum állapota. Egy tagfüggvény számára a const minősítés megadásához gyakran a működésének a funkcionális elemzése szükséges. Példa: CPPEX29 Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
40
Példa konstans tagfüggvényre 1. // Csapda van benne! class CString1 { char* m_pchElemek; public: CString1(const char* szoveg); ~CString1(void); char& operator[] (const int index)const; int hossz(void) const; }; Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
41
Példa konstans tagfüggvényre 2. //Latszolag konstans tagfuggveny, mert //nincs benne ertekadas egy adattagra sem
char& CString1::operator[] (const int index) const { return m_pchElemek[index];} // Ez egy igazi const tagfuggveny int CString1::hossz(void) const { return strlen(m_pchElemek);} Ficsor Lajos
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
42
CBEV/ 14
Miskolci Egyetem Általános Informatikai Tanszák
Példa konstans tagfüggvényre 3. Hibás használatot tesz lehetővé: const CString1 nevem("Ficsor"); cout << nevem[2] << "\n"; // OK! // A forditoprogram szamara OK, // mert const tagfuggvenyt hivunk // meg const objektumra nevem[0] = 'X'; // nevem.operator[](0) = X; Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
43
Példa konstans tagfüggvényre 4. Nem megoldás, hogy "elvesszük" a const minősítést az operator[] függvénytől: const CString2 nevem("Ficsor"); cout << nevem[2] << "\n; A fordítóprogram hibát jelez, hiszen egy const objektumra nem const tagfüggvényt próbáltunk meghívni. Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
44
Példa konstans tagfüggvényre 5. Megoldás: külön változatot definiálunk az operator[] függvényből a konstans és a nem konstans objektumok számára char& operator[] (const int index); const char& operator[] (const int index) const;
Ficsor Lajos
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
45
CBEV/ 15
Miskolci Egyetem Általános Informatikai Tanszák
Példa konstans tagfüggvényre 6. CString1 szoveg("szoveg"); // Korrekt, a // char& operator[] (const int index) // hivodik meg */ szoveg[0] = 'S';
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
46
const CString1 en("Ficsor"); // Korrekt: a // const char& operator[] (const int // index) const valtozat hivodik meg, // ezert hibajelzest kapunk en[0] = 'S'; A hibajelzést az okozza, hogy az értékadás baloldalán a const char& típusú függvényérték áll, és a fordítóprogram képes jelezni egy konstans érték referencián keresztül megkísérelt megváltoztatását. Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
47
Objektum átadása hivatkozás szerint 1. Az érték szerint átadott paraméterek és visszatérési érték hatékonysági szempontok miatt kerülendők. A pontos magyarázatra a későbbiekben még visszatérünk!
A megváltoztatni nem kívánt paramétereket const minősítővel kell ellátni!
Ficsor Lajos
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
48
CBEV/ 16
Miskolci Egyetem Általános Informatikai Tanszák
Referencia objektum helyett 1. Ne adjunk vissza referenciát, ha értéket kell visszaadni. Ez gyakran érvénytelen hivatkozás visszaadását jelenti.
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
49
Ne adjuk vissza lokális objektum címét 1. Helyes implementáció: class CKomplex { double m_valos; double m_kepzetes; public: CKomplex(double v, double k); CKomplex operator+(const Komplex& masik) {return CKomplex(m_valos + masik.m_valos, m_kepzetes + masik.m_kepzetes); } Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
50
Ne adjuk vissza lokális objektum címét 2. Helytelen implementáció: CKomplex& operator+(const CKomplex& masik) { CKomplex eredmeny(m_valos + masik.m_valos, m_kepzetes + masik.m_kepzetes); return eredmeny; }
Hiba: megszűnt objektum referenciája! Ficsor Lajos
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
51
CBEV/ 17
Miskolci Egyetem Általános Informatikai Tanszák
Ne adjuk vissza lokális objektum címét 3. Másik helytelen implementáció: CKomplex& operator+(const CKomplex& masik) { CKomplex* eredmeny = new CKomplex ( m_valos + masik.m_ valos, m_kepzetes + masik. m_ kepzetes); return *eredmeny; }
Hiba: ki szabadítja fel a lefoglalt helyet? Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
52
Korlátozott láthatóságú adattag 1. Egy tagfüggvény ne adja vissza olyan adattag referenciáját vagy pointerét, vagy olyan pointer vagy referencia típusú adattagot, amelynek a láthatósága kisebb, mint a függvényé. Példa: class CString { private: char* m_pchElemek; public: // HIBA!!! char* elemek(void) {return m_pchElemek;} }; Ficsor Lajos
Ficsor Lajos
Osztálytervezés és implementációs kérdések I.
OTERV1 /
53
CBEV/ 18