Mutatókonverziók, heterogén kollekció
Szeberényi Imre, Somogyi Péter BME IIT szebi@iit.bme.hu
class Pont {
int x, y;
public:
Pont(int x1, int y1) : x(x1), y(y1) {}
void set(int x1, int y1) { x = x1; y = y1; }
};
class Pont3D : public Pont {
int z; // Bővült
public:
Pont3D(int x1, int y1, int z1) : Pont(x1, y1), z(z1) {}
void set(int x1, int y1, int z1) { Pont::set(x1, y1);
z = z1;
}
};
put
, get
push
, pop
class Queue {
// ...
public:
void put(int e);
int get();
};
class Stack {
//...
public:
void push(int e);
int pop();
};
#include <cstddef>
class Stack : private Queue { // privát: eltakar a külvilág felé
size_t nelem;
public:
Stack() : nelem(0) {}
int pop() { nelem--; return (get()); }
// Továbbhívjuk a get()-et ^
void push(int e) { put(e); // betesz
for (int i = 0; i < nelem; i++) { put(get()); } // átforgat
// ^ Nem hatékony, csak példa!
nelem++;
}
};
int main() { Stack s1; s1.pop(); } // s1.get() helyett
Alakzat::rajzol();
Mit hív?
NULL
pointer!class Pont {
protected:
int x, y; // értékadás...
public:
Pont(int x1 = 0, int y1 = 0) { x = x1; y = y1; }
// Legyen default, mert...^
};
class Pont3D : public Pont {
int z;
public:
Pont3D(int x1, int y1, int z1) {
x = x1; y = y1; z = z1; } // Mindig lehet így?
}; // Alaposztály konstruktora mikor hívódik?
class Pont {
protected:
int x, y;
public:
Pont(int x1, int y1) : x(x1), y(y1) { }
// Itt most nem fontos a paraméter nélküli, mert paraméterest hívunk.
};
class Pont3D : public Pont {
int z;
public:
Pont3D(int x1, int y1, int z1) : Pont(x1, y1), z(z1) { }
// Alaposztály konstruktora ^
};
class Kor : public Pont {
double &r;
const double PI;
int x, y;
public:
Kor(int x, int y, double &r) : x(x), y(y), r(r), PI(3.14) {}
// Melyik y? Van már this? ^
// Kötelező inicializálni! ~~~~ ~~~~~~~
// error: field 'y' will be initialized after field 'r'
// error: constructor for 'Kor' must explicitly initialize
// the base class 'Pont' which does not have a default constructor
};
class FixKor : public Pont {
double &r;
static const double PI;
public:
FixKor(int x, int y, double &r1) : Pont(x, y), r(r1) {}
};
const double FixKor::PI = 3.14; // statikus tag, létre kell hozni
int main() {
double r = 5.0;
FixKor k = FixKor(1, 2, r);
}
Az egyparaméteres konstr. egyben automatikus konverziót is jelentenek:
pl: String a = "vlmi";
→ String a = String("vlmi");
String(int)
– konstruktor, ami megadja a string
hosszát, de nincs String(char)
konstruktor;String b = 'x';
→ String b = String(int('x'));
nem biztos, hogy kívánatos.Az aut. konverzió az explicit kulcsszóval kapcsolható ki. (pl: explicit String(int i);)
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 i) { cout << "B valami int " << i << endl; }
};
int main() { B b;
b.valami(); // B valami
b.valami(1); // B valami int 1
b.semmi(); // A semmi
b.A::valami(); // A valami
b.A::valami(3); // error: too many arguments to function
// call, expected 0, have 1
}
A kompatibilitás miatt az értékadás formálisan rendben, de az új résznek nincs helye a memóriában. Elveszik. Szeletelődés (slicing) történik.
Memóriakép rendben van, de mi a helyzet a viselkedéssel?
struct Alap { void f(); };
struct Pub : public Alap { void f(); };
struct Priv : private Alap { void f(); };
int main() {
Alap *p1B, *p2B;
Pub pub; // pub kaphat Alap-nak szóló üzeneteket.
p1B = &pub; // nem kell explicit típuskonverzió
p1B->f(); // alap o. f() elérhető
Priv priv; // priv nem érti Alap üzeneteit pl: priv.Alap::f()
p2B = (Alap *)&priv; // explicit konverzió
p2B->f(); // így már érti
}
Viselkedés és a memóriakép is kompatibilis.
struct Alap { void f(); };
struct Pub : public Alap { void f(); };
struct Priv : private Alap { int a; };
Alap alap;
Pub *p1D;
Priv *p2D;
int main() {
p1D = (Pub *)&alap;
p1D->f(); // ?????
p2D = (Priv *)&alap;
p2D->a = 0; // ?????
}
Mutatókonverzió = rejtett objektumkonverzió
class Alakzat { /*...*/ virtual void rajz() = 0; void k(); };
class Szakasz : public Alakzat { void rajz(); void k(); };
class Kor : public Alakzat { void rajz(); void k(); /*...*/ };
int main() {
Alakzat* tar[100];
tar[0] = new Szakasz(/*...*/); // konverzió, (kompatibilis)
tar[1] = new Kor(/*...*/); // konverzió, (kompatibilis)
// ...
for (int i = 0; i < 100; i++ ) {
tar[i]->rajz();
// ^ Származtatott o. fv.
tar[i]->k();
// ^ Alap oszt. függvénye
}
}
class Event {
bool checked;
public:
Event() : checked(false) {}
virtual void show() { cout << " Checked: " << checked << endl;
checked = true; } // Hurok?
virtual ~Event() {};
};
class Event1 : public Event { // ...
public:
Event1();
void show() { cout << /*...*/; Event::show(); } // Hurok?
};
class EventList {
size_t nevent; Event *events[100];
public:
EventList() : nevent(0) {}
void add(Event *e) { // Alaposztály pointereket tárolunk
if (nevent < 100) { events[nevent++] = e; }
}
void list() {
for (size_t i = 0; i < nevent; i++) { events[i]->show(); }
// Származtatott osztály függvénye ^
}
~EventList() {
for (size_t i = 0; i < nevent; i++) { delete events[i]; }
// Megszűnik az esemény is (komponens reláció) ^
}
};
class Event { /*...*/ };
class Event1 : public Event { /*...*/ }; // Új esemény:
class Event2 : public Event { /*...*/ }; // csupán definiálni
class Event9 : public Event { /*...*/ }; // kell az új osztályt
class EventList {
public:
void add(Event * e) { } // "Rábiztuk" a list-re
void list() { /*...*/ }
};
int main() {
EventList list;
list.add(new Event1(/*...*/)); list.add(new Event2(/*...*/));
/*...*/ list.add(new Event9(/*...*/)); list.list();
//
}
#include <cstddef>
class EventList {
size_t nevent; Event *events[100];
public: // ...
void add(Event *e) {
if (nevent < 100) { events[nevent++] = e; } else { delete e; throw "Megtelt!"; } }
void list() {
for (size_t i = 0; i < nevent; i++) { events[i]->show(); }
}
~EventList() {
for (size_t i = 0; i < nevent; i++) { delete events[i]; } }
// list.add(new Event1(....)); ^
// -> ~Event(); Virtuális kell! ^
}; //...
class Event {
public:
virtual void show() {}
virtual ~Event() {} // 2.
};
class Event1 : public Event { int *p;
public:
Event1(int s) { p = new int[s]; }
void show() {}
~Event1() { delete[] p; } // 1.
// Event::~Event() (2.) ^ hívódik utána
};
int main() { Event *ep = new Event1(120);
ep->show(); /*Event1::show()*/ delete ep; /* Event1::~Event1() */
}
Virt. destr. más, mint a többi virt. fv., mert az ős destruktora mindig meghívódik! https://git.ik.bme.hu/Prog2/eloadas_peldak/ea_06
https://git.ik.bme.hu/Prog2/eloadas_peldak/ea_06 CppBolt
class EventList {
int ne; // negatív?
Event events[100]; // nem pointert tárol!
public:
EventList() { ne = 0; }
void add(Event &e) { if (ne < 100) { events[ne++] = e; } }
// Adatvesztés! ^
// Szeletelés (slicing) A származtatott rész elveszik!
void list() { for (int i = 0; i < ne; i++) { events[i].show(); } }
// Event::show() ^
};
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.
Conn
osztály dinamikus tömbje)Message
objektum) továbbítása, a működést (viselkedést) megvalósító függvény elérése (a set virtuális függvényen keresztül).class Obj {
String 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.
public:
Obj(const char *n, Pin k) : nev(n) { konn = new Conn[nk = k]; }
virtual ~Obj() { delete[] konn; } // tömb felszab.
void setNev(const char *n) { nev = String(n); } // név beáll.
void setConn(Pin k, Obj &o, Pin on); // összekapcs.
void uzen(Pin k, Message msg); // üzen
virtual void set(Pin n, Message msg) = 0; // működtet
};
class Conn {
Obj *obj; // ezen objektumhoz kapcsolódik
// ^ Miért nem referencia ?
Pin n; // erre a pontra
public:
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
};
(Obj::konn)
írja le egy objektum összes kapcsolatátundef
, jel 0
és jel 1
értéke van.Műveletei:
msg1 == msg2 msg1 != msg2
msg1 + msg2 --msg
struct Message {
enum msgt { undef, jel } typ; bool J; int c;
Message(msgt t = undef, bool j = false, int n = MaxIter)
: typ(t), J(j), c(n) {
} // két üzenet egyenlő, ha az típusuk és jelszintjük is azonos
bool operator==(const Message &m) const { return (typ == m.typ && J == m.J); }
bool operator!=(const Message &m) const { return (!operator==(m)); }
Message operator+(const Message &m) const {
return Message(std::max(typ, m.typ),
(static_cast<int>(J) + static_cast<int>(m.J)) != 0,
std::max(c, m.c)); }
Message &operator--() { // pre-dekremens op.
if (--c <= 0) { throw "Sok Iteracio!"; } // iterációs számláló csökkentése
return (*this);
}
};
/**
* Üzenet (msg) küldése a k. pontra kapcsolódó obj. felé
*/
void Obj::uzen(Pin k, Message 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
}
}
A diagram szerkesztéséhez az UMLet (http://www.umlet.com/) programot használtam.
class Drot : public Obj {
protected: // megengedjük a származtatottnak
Message veg[2]; // két vége van, itt tároljuk az üzeneteket
public:
Drot(const char *n = "") : Obj(n, 2) {} // 2 végű obj. Létrehozása
Message get() const { return veg[0] + veg[1]; } // bármelyik vég
void set(Pin n, Message msg); // működtet
};
void Drot::set(Pin n, Message msg) {
if (veg[n] != msg) { // ha változott
veg[n] = msg; // megjegyezzük és
uzen(n ^ 1, msg); // elküldjük a másik végére (vezet)
// ^ Gonosz trükk!
}
}
class Csp : public Obj {
protected: // megengedjük a származtatottnak
Message veg[3]; // három vége van, itt tároljuk az üzeneteket
public:
Csp(const char *n = "") : Obj(n, 3) {} // 3 végű objektum
void set(Pin n, Message msg); // működtet
};
void Csp::set(Pin n, Message msg) {
if (veg[n] != msg) { // ha változott
veg[n] = msg; // megjegyezzük és
uzen((n + 1) % 3, msg); // elküldjük a másik 2 végére
uzen((n + 2) % 3, msg);
}
}
class Kapcsolo : public Drot { // Drótból
bool be; // állapot
public:
Kapcsolo(const char *n = "") : Drot(n), be(false) {}
void set(Pin n, Message msg);
// jel, false, lehetne undef
void kikap() { be = false; uzen(0, Message(Message::jel));
uzen(1, Message(Message::jel)); }
void bekap() { be = true; uzen(0, veg[1]); uzen(1, veg[0]); }
};
void Kapcsolo::set(Pin n, Message msg) {
if (be) { Drot::set(n, msg); } // be van kapcsolva, drótként viselk.
else { veg[n] = msg; } // ki van kapcsolva, csak megjegyezzük
}
class NAND : public Obj {
Message veg[3]; // három "vége" van
public:
NAND(const char *n = "") : Obj(n, 3) {} // 3 végű obj. létreh.
void set(Pin n, Message msg); // működtet
Message get() { return (veg[2]); } // kim. lekérdezése
};
void NAND::set(Pin n, Message msg) {
if (n != 2 && veg[n] != msg) { // ha változott bemenet
veg[n] = msg; // megjegyezzük
uzen(2, veg[2] = Message(Message::jel, !(veg[0].J * veg[1].J),
// kimenet előállítása ^
msg.c)); // üzenünk a kimeneten
// ciklusszám marad ^
}
}
Class R_S_FF : public Obj {
protected:
Message veg[4]; // négy "vége" van
NAND N[2]; // két db NAND kapu, komponens
public:
R_S_FF(const char *n) : Obj(n, 4) {
N[0].setConn(2, N[1], 0); // összekötések létrehozása
N[1].setConn(2, N[0], 0);
}
void set(Pin n, Message msg); // működtet
Message get(int i) { // kimenet lekérdezése
if (i >= 2) { i = 0; }
return (veg[i + 2]);
}
};
Void R_S_FF::set(Pin n, Message msg) {
if (n < 2 && veg[n] != msg) { // ha input és változott,
veg[n] = msg; // letárolja
N[n].set(1, msg); // megfelelő bemenetre küldi
uzen(2, veg[2] = N[0].get()); // üzen a kimeneten
uzen(3, veg[3] = N[1].get()); // üzen a kimeneten
// ^ Kimenetek előállítása, mert bent nincs csomópont.
}
}
Kapcsolo K1("K1"), K2("K2"); Forras F1("F1"), F2("F2");
R_S_FF FF("FF");
try { F1.setConn(0, K1, 0); FF.setConn(0, K1, 1);
F2.setConn(0, K2, 0); FF.setConn(1, K2, 1);
F1.init(); F2.init(); K1.bekap(); K2.bekap();
cerr << FF.get(0).J << FF.get(1).J << FF.get(2).J << FF.get(3).J << endl;
K1.kikap();
cerr << FF.get(0).J << FF.get(1).J << FF.get(2).J << FF.get(3).J << endl;
K1.bekap();
cerr << FF.get(0).J << FF.get(1).J << FF.get(2).J << FF.get(3).J << endl;
K2.kikap();
cerr << FF.get(0).J << FF.get(1).J << FF.get(2).J << FF.get(3).J << endl;
} catch (const char *s) { cerr << s << endl; }
https://git.ik.bme.hu/Prog2/eloadas_peldak/ea_06 digit
Öröklés