Feladat Készítsünk programot, amely ciklikusan egy egyenest forgat 8 fokonként, mialatt 3 db vektort mozgat és forgat 5, 6 ill 7 fokonként, és kijelzi a szituációkat, amikor valamelyik vektor és az egyenes párhuzamos.
Objektumok számbavétele (Milyen dolgok szerepelnek a leírásban?) Dolgok - főnevek Vektor, egyenes, szituáció Vektor -3db Különböző vektorok ugyanolyan jellegű dolgok, azaz ugyanannak a típusnak különböző példányai.˙ Szituáció nem önálló objektum vektor és egyenes között fennálló viszony, asszociáció.
Következtetés Fontos objektumok két típusba sorolhatók: Vector Line
Objektumok absztrakt definíciója Objektumokon végezhető műveletek (egy típushoz tartozó különböző objektumokon ugyanolyan műveletek végezhetők, ezért a műveleteket típusokhoz rendeljük). Műveletek - igék Vektor : forgatás, eltolás. Egyenes: forgatás. Párhuzamosság vizsgálat (Vektor, v. egyenes?)
Objektumok belső szerkezete Objektumok belső tulajdonságának megállapítása a leírásból nem lehetséges Korábbi ismeretekre kell támaszkodnunk! (Ez a tipikus!) Két dimenziós vektor: x és y koordináta. Egyenes: egy pontjának és irányvektorának két-két koordinátája.
Elemzés eredménye Objektum Típus Attribútumok Felelősség vektor(ok) Vector x, y vektor forgatása, eltolása, párhuzamosság? egyenes Line x0,y0,vx,vy egyenes forgatása,
Implementáció C++-ban Az osztály olyan általánosított struktúra, amely egységbe zárja az adatokat és a műveleteket és alapértelmezésben minden tagja- függetlenül attól, hogy adatról, vagy függvényről van-e szó - az osztályon kívülről elérhetetlen.
Egységbe zárás C++-ban értelmezése class Vector { //private: double x, y; //adatok, állapot public: void Set (double x, double y ) ; void Translate ( Vector d ) ; void Rotate (double fi ) ;
Vector v; v.Set ( 3.0, 4.0); v.x = 6.0; //Fordítási hiba!
Tagfüggvények implementációja deklaráció és definíció - osztályon belül definíció- osztályon kívül (deklaráció osztályon belül) class Vector { double x, y; //adatok, állapot public: void Set (double x0, double y0 ) {x = x0; y = y0;} void Rotate (double ) ; //csak deklaráció }; void Vector::Rotate (double fi) { //definíció double nx = cos(fi)*x + sin(fi)*y; //x,y saját adat double ny = -sin(fi)*x + cos(fi)*y; x = nx; y = ny ; //vagy this -> y = ny; }
Megjegyzések tagfüggvények számára az adattagok közvetlenül elérhetők. Tagfüggvény osztályon kívüli definíciójakor jelezni kell, hogy melyik osztályhoz tartozik: scope(::) operátor. Pl.: Vector::Rotate() Osztályon belül definiált függvény automatikusan inline függvény lesz. Minden függvényben létezik egy nem látható paraméter a this. A this mindig az aktuális objektumra mutató pointer.
osztály - struktúra hasonlósága osztály adattagjai osztály - struktúra hasonlósága osztály adattagjai -> struktúra osztály függvénymezői-> globális függvények
Globális függvények Pl.: Vector osztály Set függvényének a neve: Set_Vector_dbldbl . Mivel mindegyik más objektumon dolgozik 1000 db objektum példány esetén 1000 db függvény kellene. Megoldás: egy függvény, amely paraméterként megkapja az adatokat összefogó struktúra kezdőcímét, azaz az objektum saját címét.
Példa class Vector { double x, y; //adatok, állapot public: void Set (double x0, double y0 ) {x = x0; y = y0;} void Rotate (double ) ; //csak deklaráció struct Vector { double x, y }; void Set_Vector (struct Vector *this, double x0, double y0) { this -> x = x0; this -> y = y0; } void Rotate_Vector (Vector *this, double fi ) { ... }
Objektumok létrehozása Vector osztály alapján objektumokat definiálhatunk. Az objektumokat értékadásban felhasználhatjuk Tagfüggvények segítségével üzeneteket küldhetünk nekik.
Konstruktor és destruktor class Vector { double x, y; public: Vector(double x0, double y0) {x=x0; y=y0; } //konstruktornak nincs visszatérési típusa ~Vector() { } //destruktornak nincs típusa sem argumentuma
Objektumok létrehozása Konstruktorral és destruktorral felszerelt Vector osztály alapján definiált objektumok használata: { Vector v1(0.0, 1.0 ); //konstruktor hívás Vector v2 = v1; ...... v1 = Vector (3.0, 4.0 ) ; //ideiglenes objektum létrehozása és v1-hez rendelése //destruktor az ideiglenesre // 2db destruktor hívás: v1, v2
Konstruktor és destruktor Alapértelmezés szerinti konstruktor - argumentumot nem vár (Ha egyáltalán nem definiálunk, akkor a fordító egy ilyet létrehoz. Objektumok élettartama- születésük, megszűnésük. Globális objektumok (argumentuma csak konstans kifejezés lehet
Objektumok tömbje 100 db Vector objektum: Vector v [100]; Mivel a konstruktor argumentumai nem átadhatók, kell definiálni egy alapértelmezésű konstruktort is.
Objektumok dinamikus definiálása new és delete operátorok használata Egy dinamikusan létrehozott objektum a new operátor alkalmazásakor születik meg és a delete alkalmazásakor szűnik meg. ( A konstruktor és destruktor is ekkor fut le.). Példa: Vector *pv = new Vector (1.5, 1.5); Vector *av = new Vector [100 ];
Dinamikus tömbök Tömb esetében a delete hatására a memória felszabadul, de csak a legelső elemre fut le a destruktor delete [ ] av; //mind a 100 elemre lefut a destruktor
A védelem enyhítése - a barát (friend) mechanizmus Párhuzamosság ellenőrzése -AreParallel függvény Kérdések - Vector osztályhoz tartozzon - Line osztályhoz tartozzon - egyikhez sem tartozzon, legyen globális függvény és mindkét objektumot argumentumként kapja.
friend mechanizmus Probléma: AreParallel() egyik objektum adattagjaihoz sem férhet hozzá. Megoldás: engedélyezhetjük egy idegen függvény vagy egy osztály összes tagfüggvénye számára az adattagokhoz való hozzáférést. Ezt a friend kulcsszó teszi lehetővé.
Függvény számára a hozzáférés biztosítása class Line { double x0, y0, vx, vy ; public: ..... friend Bool AreParallel (Line, Vector ); }; class Vector { double x, y; public: ... friend Bool AreParallel (Line, Vector ); }; Bool AreParallel ( Line l, Vector v) { return ( l.vx * v.y == l.vy * v.x ); }
Osztály számára a hozzáférés biztosítása A Line osztály a Vector osztályt fogadja barátjának: class Vector ; class Line { friend class Vector; double x0, y0, vx, vy ; public: ..... }; class Vector { double x, y; public: ... Bool AreParallel ( Line l) { return ( l.vx * y == l.vy *x ); } }
Operátorok átdefiniálása (operator overloading) Matematikai és programnyelvi operátok többrétűsége (polimorfizmusa) közismert. + számok összeadása mátrixok összeadása A fordítást az operandusok típusának megfelelően végzi el az adott nyelv fordítóprogramja. C++ -lehetővé teszi, hogy a nyelv operátorait ne csak a beépített típusokra, hanem az osztállyal gyártott objektumokra is alkalmazzuk. (Operátor overloading )
Műveletek vektorokkal Jelöljük a vektorok összeadását + jellel és az értékadást az = operátorral. Vector v, v1, v2 ; v = v1 + v2 ; Mit csinál a fordító ezen sorok hatására?
Fordító teendői - Mindenekelőtt tudja, hogy az összeadást előbb kell elvégezni, mint az értékadást. (Műveletek precedenciája) - Megvizsgálja a műveletben résztvevő operandusok típusait. (+ jel bal és jobb oldalán álló operandusok típusát).
+ összeadás műveletének elvégzése Jelen esetben mindkét operandus típusa Vector - a fordító nem rendelkezik beépített művelettel. Megnézi, hogy a baloldali objektum osztályának (Vector) van-e olyan összeadás tagfüggvénye, azaz operator+ tagfv-e, amellyel neki a jobboldali objektumot el lehet küldeni. Megnézi, hogy van-e olyan operator+ globális függvény, amely első argumentumként az első operandust, második argumentumként a másodikat várja.
Tagfüggvény, vagy globális függvény? Ez azt jelenti, hogy megnézi, hogy a Vector osztálynak létezik-e - Vector::operator+(Vector) tagfüggvénye vagy (kizáró vagy) - operator+(Vector, Vector ) globális függvény
Műveletek vektorokkal A v1 + v2 kifejezés az alábbi függvényhívással ekvivalens ( a változattól függően): - v1.operator+ ( v2 ); //Vector:: operator+(Vector); //tagfüggvény - operator+ ( v1, v2 ); //operator+ (Vector,Vector); //globális függvény
= jel operátor feldolgozása Az egyenlőség két operandusú (ezt a fordító tudja.) A bal oldalon a v objektumot találja, amely Vector típusú. A jobb oldalon az összeadásnak megfelelő függvényhívás van. Ez olyan típust képvisel, amilyet az összeadás függvény visszaad. Ezt a visszatérési típust ugyancsak Vector-nak kell definiálni. Megvizsgálja a fordító, hogy létezik-e operator= tagfüggvénye a bal oldali objektumnak, melynek a jobb oldali objektumot elküldhetjük Vector::operator=(Vector).
+ művelet vektorokkal Van-e globális függvény? Néhány operátornál a globális függvény használata nem megengedett Pl.: értékadás =, index [ ], függvényhívás () indirekt mezőválasztó -> operátorok. A helyettesítés: v.operator= (v1.operator+ ( v2 ) ) ; vagy v.operator= (operator+ ( v1, v2 ) ) ; Ha a fordító nem talál megfelelő függvényt, hiszen az = operátornak van alapértelmezése, mégpedig az adatmezők bitenkénti másolása.
Operátor átdefiniálás tagfüggvénnyel class Vector { double x, y ; public: Vector ( double x0, double y0 ) {x=x0; y=y0;} Vector operator+ (Vector v) ; }; Vector Vector:: operator+(Vector v) { Vector sum (v.x+x, v.y+y ) ; return sum ; };
Operátor+ tagfüggvény megvalósítása Az összeadás függvény belsejében létrehoztunk egy ideiglenes objektumot, melynek attribútumait a konstruktorának segítségével a bal (üzenet célja) és a jobb (üzenet paramétere) operandusok x és y koordinátáinak az összegével inicializáltuk.Közvetlen hozzáférés a v objektum privát mezőihez: (Lehetséges?) Közvetlen elérhetőség érvényes a családtagokra is. (Minden ugyanezen osztállyal definiált objektumra).
Összeadás üzenetre egy rossz megoldás Vector Vector:: operator+(Vector v) { x+ = v.x ; y+ = v.y ; return *this ; v értéke jó lesz, de v1 is elromlik!
Operátor-átdefiniálás globális függvénnyel class Vector { double x, y ; public: Vector ( double x0, double y0 ) {x=x0; y=y0;} friend Vector operator+ (Vector& v1,Vector& v2) ; }; Vector operator+(Vector& v1, Vector& v2) { Vector sum (v1.x+v2.x, v1.y+v2.y ) ; return sum ; }
Operátor - átdefiniálás Tagfüggvénnyel vagy globális függvénnyel? OO program működése lényegében az objektumok közötti üzenetváltásokkal valósul meg. A globális függvények nem illenek ebbe a koncepcióba. Néha nincs más megoldás!
Példa arra, amikor csak globális függvényt használhatunk: Skalár vektor szorzás Vector v1, v2 (3.0, 4.0 ); v1 = 2 * v2 ; A bal oldalon most nem objektum, hanem egy beépített típus, int van, annak egy operator*(Vector) tagfüggvényt nem tudunk definiálni.
Operátor átdefiniálás szabályai A C++-ban szinte minden operátor átdefiniálható, kivéve: - tagkiválasztó “.” - érvényességi kör (scope):: - a feltételes választás ( ? : ) Az átdefiniálás során figyelembe veendő szabályok: - a szintaxis nem változtatható meg - az egyoperandus·/kétoperandus· tulajdonság nem változtatható meg - a precedencia nem változtatható meg.
Konverziós operátorok Az átdefiniálható operátorok külön családját képezik az ún. konverziós (cast) operátorok. Konverzió kijelölése: (céltípus) változó Értékadásban (ha két oldalon eltérő típus van) és függvényhíváskor (átadott objektum típusa a deklarációban megadottól eltér) automatikus típuskonverzió megy végbe. Implicit típuskonverzió.
Konverziós operátorok átdefiniálása Példa: Két double koordinátát tartalmazó vektor objektumok és az MS_Windows-ban megszokott forma ( egy vektor egy long típusban kerül tárolásra úgy, hogy a long alsó helyiértékű 16 bitje az x, felső helyiértékű 16 bitje az y koordináta) között végzünk átalakításokat.
Típuskonverzió Vector vec ( 1.0, 2.5 ) ; long point = 14L + 36 L << 16 ) ; //14, 36 koordinátájú pont extern f (long) ; extern g (Vector) ; vec = (Vector ) point ; //explicit vec = point ; //implicit g (point ) ; //implicit point = (long ) vec ; //explicit point = vec ; //implicit f (vec ) ; //implicit
Konverziós operátorok átdefiniálása Újdonság a C++-ban: osztállyal gyártott objektumokra is definiálhatunk átalakító operátorokat. Két eset: - osztállyal definiált típusról alakítunk át más típusra ( ami lehet más osztállyal definiált típus vagy beépített típus). - Beépített vagy osztállyal definiált típust konvertálunk osztállyal definiált típusra.
Konverzió osztállyal definiált típusról A Vector -> típus (jelen esetben long) átalakítást kell megvalósítanunk, amely a Vector osztályban egy operator típus ( ) ; tagfüggvénnyel lehetséges. Ehhez a tagfüggvényhez nem lehet visszatérési típus és nem lehet argumentum sem.
Megvalósítás class Vector { double x, y ; public: operator long ( ) { return ((long) x + (long) y <<16 ) ; } }
Beépített vagy osztállyal definiált típust konvertálunk osztállyal definiált típusra Amennyiben egy objektumot akarunk létrehozni egy más típusú változóból, olyan konstruktort kell készíteni, ami argumentumként a megadott típust fogadja el. A long -> Vector átalakításhoz tehát a Vector osztályban egy long argumentum· konstruktor szükséges:
Példa class Vector { double x, y ; public: Vector ( long lo) { x = lo & 0xffff; y = lo >>16 ; }