Programozás alapjai II. (6. ea) C++ mutatókonverziók, heterogén kollekció Szeberényi Imre BME IIT <szebi@iit.bme.hu> Öröklés (ismétlés) Egy osztályból olyan újabb osztályokat származtatunk, amelyek rendelkeznek az eredeti osztályban már definiált tulajdonságokkal és viselkedéssel. Analitikus Korlátozó A tagfüggvények átdefiniálhatók (overload) virtuális függvény: hogy a tagfüggvény alaposztály felől is elérhető legyen M Ű E G Y T E M 7 8 C++ programozási nyelv BME-IIT Sz.I..3.. -- C++ programozási nyelv BME-IIT Sz.I..3.. -- Analitikus öröklés példa (ism.) class Pont { int x, y; Pont(int x, int y) :x(x), y(y) { void set(int x, int y) {x = x; y = y; class Pont3D :public Pont { int z; Pont3D(int x, int y, int z) Bővült :Pont(x, y), z(y) { void set(int x, int y, int z) { Pont::set(x, y); z = z; Pont int x, y Pont3D int z C++ programozási nyelv BME-IIT Sz.I..3.. -3- Korlátozó öröklés példa/ (ism.) Szeretnénk egy stack és egy queue osztályt: mindkettő tároló nagyon hasonlítanak, de eltér az interfészük: o put, get <> push, pop önállóak vagy örökléssel? class Queue {. void put( int e ); int get( ); class Stack {. void push( int e ); int pop( ); C++ programozási nyelv BME-IIT Sz.I..3.. -4- Korlátozó öröklés példa/ (ism.) class Stack : private Queue { // privát: eltakar a külvilág felé int nelem; Továbbhívjuk a Get()-et Stack( ) : nelem() { int pop( ) { nelem--; return(get()); Queue void push(int e) { put(e); // betesz for( int i = ; i < nelem; i++ ) put( ),get( ) put(get( )); // átforgat nelem++; Stack Nem hatékony, csak példa! push( ), pop( ) Stack s; s.pop(); s.get() C++ programozási nyelv BME-IIT Sz.I..3.. -5- Vonal végpont rajzol() Virtuális tagfüggvény (ism.) Alakzat poz, szin mozgat() virtual rajzol() Kör sugár rajzol() rajzol() "átdefiniálása" virtuális függvénnyel átdefiniálása: fv overload Alakzat::rajzol(); Mit hív? késői kötés C++ programozási nyelv BME-IIT Sz.I..3.. -6-
Fontos C++ sajátságok Konstruktor nem lehet virtuális Destruktor igen, és érdemes odafigyelni rá Alaposztályból dinamikus adattagot tartalmazó osztályt hozunk létre, majd ezt az alaposztály "felöl" használjuk (töröljük). A konstruktorból hívott (saját) virtuális függvény még nincs átdefiniálva! A virt. táblát maga konstruktor tölti ki! (kötés) absztrakt osztály estén NULL pointer! class Pont { protected: int x, y; Inicializálás / (ism.) Pont(int x =, int y = ) { x = x; y = y; class Pont3D :public Pont { int z; Pont3D(int x, int y, int z) Legyen default, mert. Mindig lehet így? {x = x; y = y; z = z; Alaposztály konstruktora mikor hívódik? C++ programozási nyelv BME-IIT Sz.I..3.. -7- C++ programozási nyelv BME-IIT Sz.I. 9.3.6. -8- Inicializálás / (ism.) class Pont { protected: int x, y; Pont(int x, int y) : x(x), y(y) { class Pont3D :public Pont { int z; Nem fontos a default. Alaposztály konstruktora Pont3D(int x, int y, int z) : Pont(x, y), z(z) { Inicializálás /3 (ism.) class FixKor :public Pont { double& r; Kötelező inicializálni! const double PI; Kor(int x, int y, double& r;) :x(x), y(y), r(r), PI(3.4) { Melyik y? Van már this? class FixKor :public Pont { double& r; static const double PI; const double Kor::PI = 3.4; // statikus tag, létre kell hozni C++ programozási nyelv BME-IIT Sz.I. 9.3.6. -9- C++ programozási nyelv BME-IIT Sz.I..3.. -- Öröklés és polimorfizmus struct A { void valami() { cout << "A valami" << endl; void semmi() { cout << "A semmi" << endl; struct B: public A{ void valami() { cout << "B valami" << endl; void valami(int) { cout << "B valami int" << endl; B b; b.valami(); // B valami b.valami(); // B valami(int) b.semmi(); // A semmi b.a::valami(); // A valami b.a::valami(int) // HIBA Mutatókonverzió örökléskor Mutatókonverzió = rejtett objektumkonverzió Kompatibilitás: öröklés kompatibilis memóriakép kompatibilis viselkedés (tagfüggvények) class Base {. class PublicDerived : public Base {. class PrivateDerived: private Base {. C++ programozási nyelv BME-IIT Sz.I..3.. -- C++ programozási nyelv BME-IIT Sz.I..3.. --
Base * pb viselkedés kompatibilis? Konverzió alaposztályra alap új rész PublicDerived vagy PrivateDerived memóriakép kompatibilis PublicDerived pubd; // pubd kaphatja a Base üzeneteit Base * pb = &pubd; // nem kell explicit típuskonverzió PrivateDerived prid;// prid nem érti a Base üzeneteit pb = (Base *)&prid;// mégis érti explicit konverzió! C++ programozási nyelv BME-IIT Sz.I..3.. -3- Konverzió származtatott osztályra Derived * pd viselkedés NEM kompatibilis alap új rész Base memóriakép NEM kompatibilis Base base; Derived *pd = (Derived *) &base; // nem létező adatmezőket és üzeneteket el lehet érni // veszély: explicit konverzió C++ programozási nyelv BME-IIT Sz.I..3.. -4- Mutatókonverzió és virtuális fv. class Alakzat { virtual void rajzol() = ; class Vonal : public Alakzat { class Kor : public Alakzat { class Teglalap : public Alakzat { Alakzat* tar[]; tar[] = new Vonal(.); // konverzió tar[] = new Kor(); // konverzió. for (int i = ; i < ; i++ ) tar[i] ->rajzol(); Származtatott osztály függvénye Heterogén gyűjtemények Különböző típusú objektumokat egy közös gyűjteménybe tesszük Egységes kezelés: valamilyen viselkedési kompatibilitás egy öröklési hierarchiából definiált objektumokat tehetünk heterogén szerkezetbe kompatibilitásból származó előnyök (pl. alaposztály pointere) kihasználása C++ programozási nyelv BME-IIT Sz.I..3.. -5- C++ programozási nyelv BME-IIT Sz.I..3.. -6- Heterogén kollekció példa Eseménykezelő megvalósítása Egy rendszer eseményeit kell naplózni. Az események egymástól eltérő adattartamúak, és esetleg új események is lesznek, amit még nem ismerünk. Események sorrendje fontos, ezért célszerűen egy tárolóban kell lenniük. Az eseménynapló megnézésekor meg kell mutatni azt is, hogy mely eseményeket néztük meg már korábban. Event * checked show( ) Event Event.... show( ) show( ) EventList add( ), list( )... Még nem ismerjük, de nem is kell! C++ programozási nyelv BME-IIT Sz.I..3.. -7- C++ programozási nyelv BME-IIT Sz.I..3.. -8-
Eseménykezelő megvalósítása/ class Event { bool checked; Event ( ) :checked(false) { virtual void show( ) { cout<<" Checked: "; cout<<checked<<endl; checked = true; class Event :public Event { Hurok? Event(); void show ( ) { cout <<. ; Event::show(); Eseménykezelő megvalósítása/3 class EventList { int nevent; Event *events[]; EventList( ) { nevent = ; void add(event& e) { events[nevent++] = &e; void add(event *e) { events[nevent++] = e; void list( ) { for (int i = ; i < nevent; i++) events[i]->show(); Származtatott osztály függvénye Alaposztály mutatója Nem célszerű mind a két fajta add()! C++ programozási nyelv BME-IIT Sz.I..3.. -9- C++ programozási nyelv BME-IIT Sz.I..3.. -- Eseménykezelő használata class Event { class Event :public Event { class Event :public Event { class EventList { EventList list; list.add(new Event(.)); list.add(new Event(.)); list.add(new Event9(.)); Event e; list.add(e); list.list(); Ez másik add()! (ref) Új esemény: csupán definiálni kell az új osztályt Heterogén kollekció összefoglalás Különböző típusú objektumokat egy közös gyűjteménybe tesszük. Kihasználjuk az öröklésből adódó kompatibilitást. Nagyon gyakran alkalmazzuk könnyen bővíthető, módosítható, karbantartható C++ programozási nyelv BME-IIT Sz.I..3.. -- C++ programozási nyelv BME-IIT Sz.I..3.. -- Tipikus hiba class EventList { Nem pointert tárol! int nevent; Event events[]; EventList( ) { nevent = ; void add(event& e) { events[nevent++] = e; void add(event* e) { events[nevent++] = *e; void list( ) { for (int i = ; i < nevent; i++) events[i].show(); Event::show() Adavesztés!! A származtatott rész elveszik! C++ programozási nyelv BME-IIT Sz.I..3.. -3- Ki szabadít fel? class EventList { int nevent; Event *events[];. void add(event* e) { events[nevent++] = e; void list( ) { for (int i = ; i < nevent; i++) events[i]->show(); ~EventList() { for (int i = ; i < nevent; i++) delete events[i]; // events[i]->~event();. Virtuális kell! list.add(new Event(.)); C++ programozási nyelv BME-IIT Sz.I..3.. -4-
Virtuális destruktor újból Digitális áramkör modellezése class Event { virtual void show( ) { ; virtual ~Event() { ; Event *ep = new Event(); delete ep; class Event :public Event { int *p; Event(int s) { p = new int[s];. ~Event() { delete[] p; Más, mint a többi virt. fv. Digitális jel: üzenet Áramköri elemek: objektumok bemenet, kimenet, viselkedés (f(x)) kapcsoló, kapu, drót, forrás, csomópont Objektumok a valós jelterjedésnek megfelelően egymáshoz kapcsolódnak. (üzennek egymásnak) Visszacsatolás megengedett. C++ programozási nyelv BME-IIT Sz.I..3.. -5- C++ programozási nyelv BME-IIT Sz.I..3.. -6- Bemenetek Modell f(x) Visszacsatolás Kimenetek A változásokat üzenetek továbbítják. Ha nincs változás, nem küldünk újabb üzenetet. Csak véges számú iterációt engedünk meg. Áramköri elemek felelőssége Kapcsolatok (bemenet/kimenet) kialakítása, nyilvántartása. Bejövő üzenetek tárolása összehasonlítás céljából. Válaszüzenetek előállítása és továbbítása a bejövő üzeneteknek és a működésnek megfelelően. C++ programozási nyelv BME-IIT Sz.I..3.. -7- C++ programozási nyelv BME-IIT Sz.I..3.. -8- Osztályhierarchia Obj Conn Drot Csp NAND S_R_FF Forras 3 3 + 4 Obj: alaposztály A különböző objektumok ősosztálya. minden áramköri elem ebből származik Lehetővé teszi ill. támogatja: az objektumok közötti kapcsolatok kialakítását, (a Conn osztályból felépített dinamikus tömbben tároljuk a kapcsolatokat) kapcsolatokon keresztül az üzenetek ( objektum) továbbítását, a működést (viselkedést) megvalósító függvény elérését (a Set virtuális függvényen keresztül). C++ programozási nyelv BME-IIT Sz.I..3.. -9- C++ programozási nyelv BME-IIT Sz.I..3.. -3-
Obj: absztrakt alaposztály class Obj { char nev[]; // objektum neve Pin nk; // kapcsolódási pontok száma Conn *konn; // kapcsolatok leírása Obj(const Obj&) { // hogy ne lehessen használni Obj& operator=(const Obj&) { // hogy ne lehessen haszn. Obj(char *n, Pin k) {setnev(n); konn = new Conn[nk = k]; ~Obj() { delete[] konn; // tömb felszab. void setnev(char *n) { strcpy(nev, n); // név beállítása void setconn(pin k, Obj& o, Pin on); // összekapcs. void uzen(pin k, & msg); // üzen virtual void set(pin n, & msg) = ; //működtet C++ programozási nyelv BME-IIT Sz.I..3.. -3- Conn: kapcsolatok tárolása Egy objektumkapcsolatot leíró osztály Példányaiból felépített dinamikus tömb (Obj::konn) írja le egy objektum összes kapcsolatát Miért nem referencia? class Conn { Obj *obj; // ezen objektumhoz kapcsolódik Pin n; // erre a pontra Conn() :obj(null) { void setconn(pin k, Obj& o) { n = k; obj = &o; // beállít Obj *getconn(pin& k) { k = n; return(obj); // lekérdez C++ programozási nyelv BME-IIT Sz.I..3.. -3- : jel mint üzenet Digitális jelet reprezentáló osztály undef, jel és jel értéke van. A végtelen iteráció elkerülése végett a jelszint mellet egy iterációs számláló is van. Megvalósítása struktúrával, mivel az adattakarás csak nehezítene. Műveletei: msg == msg msg!= msg --msg : jel mint üzenet / struct { enum msgt { undef, jel typ; // típus bool J; // jelszint v. int c; // iterációs számláló // default konstruktor is (msgt t = undef, bool j = false, int n = ) :typ(t), J(j), c(n) { // két üzenet egyenlő, ha az típusuk és jelszintjük is azonos bool operator==(& m) { return(typ == m.typ && J == m.j); bool operator!=(& m) { return(!operator==(m)); & operator--() { if (--c <= ) // iterációs számláló csökkentése throw "Sok Iteracio!"; return(*this); C++ programozási nyelv BME-IIT Sz.I..3.. -33- C++ programozási nyelv BME-IIT Sz.I..3.. -34- Üzenet továbbítása Drót obj. modellje /** * Üzenet (msg) küldése a k. pontra kapcsolódó obj. felé */ void Obj::uzen(Pin k, & msg) { Pin n; // kapcsolodó objektum kapcs. pontja if (k >= nk) throw "Uzenet hiba"; // hiba, nincs ilyen végpont if (Obj *o = konn[k].getconn(n)) { o->set(n, --msg); // szomszéd működtető függvénye Obj nev, pin, konn setnev(), setconn(), uzen(), Drot veg[] Conn obj, n setconn(), getconn() J, c ==(),!=() --() C++ programozási nyelv BME-IIT Sz.I..3.. -35- C++ programozási nyelv BME-IIT Sz.I..3.. -36-
Drót classdrot:publicobj{ protected: // megengedjük a származtatottnak veg[]; // két vége van, itt tároljuk az üzeneteket Drot(char *n = "") : Obj(n, ) { // végű obj. létrehozása void set(pin n, & msg); // működtet void Drot::set(Pin n, & msg) { if (veg[n]!= msg) { // ha változott veg[n] = msg; // megjegyezzük és uzen(n^, msg); // elküldjük a másik végére (vezet) C++ programozási nyelv BME-IIT Sz.I..3.. -37- Csomópont class Csp :public Obj { protected: // megengedjük a származtatottnak veg[3]; // három vége van, itt tároljuk az üzeneteket Csp(char *n = "") : Obj(n, 3) { // 3 végű obj. létrehozása void set(pin n, & msg); // működtet void Csp::set(Pin n, & msg) { if (veg[n]!= msg) { // ha változott veg[n] = msg; // megjegyezzük és uzen((n+)%3, msg); // elküldjük a másik végére uzen((n+)%3, msg); C++ programozási nyelv BME-IIT Sz.I..3.. -38- Kapcsoló class Kapcsolo :public Drot { // Drótból int be; // állapot Kapcsolo(char *n = "") : Drot(n), be() { void set(pin n, & msg); jel, false, lehetne undef void kikap() { be = ; uzen(, (::jel)); uzen(, (::jel)); void bekap() { be = ; uzen(, veg[]); uzen(, veg[]); void Kapcsolo::set(Pin n, & msg) { if (be) Drot::set(n, msg); // be van kapcsolva, drótként viselk. else veg[n] = msg; // ki van kapcsolva, csak megjegyezzük NAND kapu class NAND :public Obj { veg[3]; // három "vége" van NAND(char *n = "") : Obj(n, 3) { // 3 végű obj. létreh. void set(pin n, & msg); // működtet const & get() { return(veg[]); // kim. lekérdezése void NAND::set(Pin n, & msg) { if (n!= && veg[n]!= msg) { // ha változott bemenet veg[n] = msg; // megjegyezzük uzen(, veg[] = (::jel,!(veg[].j * veg[].j), msg.c)); // üzenünk a kimeneten kimenet előállítása ciklusszám marad C++ programozási nyelv BME-IIT Sz.I..3.. -39- C++ programozási nyelv BME-IIT Sz.I..3.. -4- S_ () R_ () N N Q () Q_ (3) S_-R_ tároló class S_R_FF :public Obj { protected: veg[4]; // négy "vége" van NAND N[]; // két db NAND kapu, komponens S_R_FF(char *n) : Obj(n, 4) { N[].setConn(, N[], ); // összekötések létrehozása N[].setConn(, N[], ); void set(pin n, &msg); // működtet const & get(int i) { // kimenet lekérdezése if (i >= ) i = ; return(veg[i+]); C++ programozási nyelv BME-IIT Sz.I..3.. -4- S_ () R_ () S_-R_ tároló / N N void S_R_FF::set(Pin n, &msg) { if (n < && veg[n]!= msg) { // ha input és változott, veg[n] = msg; // letárolja N[n].set(, msg); // megfelelő bemenetre küldi uzen(, veg[] = N[].get()); // üzen a kimeneten uzen(3, veg[3] = N[].get()); // üzen a kimeneten C++ programozási nyelv BME-IIT Sz.I..3.. -4- Q () Q_ (3)
Szimulátorunk próbája Kapcsolo K("K"), K("K"); Forras F("F"), F("F"); S_R_FF FF("FF"); try { F.setConn(, K, ); FF.setConn(, K, ); F.setConn(, K, ); FF.setConn(, K, ); F F F.init(); F.init(); K.bekap(); K.bekap(); cerr << FF.get().J << FF.get().J << FF.get().J << FF.get(3).J << endl; K.kikap(); cerr << FF.get().J << FF.get().J << FF.get().J << FF.get(3).J << endl; K.bekap(); cerr << FF.get().J << FF.get().J << FF.get().J << FF.get(3).J << endl; K.kikap(); cerr << FF.get().J << FF.get().J << FF.get().J << FF.get(3).J << endl; catch (const char *s) { cerr << s << endl; K FF K C++ programozási nyelv BME-IIT Sz.I..3.. -43-