Objektummodell, öröklés, virtuális tagfüggvény
Szeberényi Imre, Somogyi Péter BME IIT szebi@iit.bme.hu
int i;
i
nevű objektum aminek a mintája intEgy adott objektumtípust többféleképpen lehet implementálni (megvalósítani).
Hagyományos nyelveken a típus érték-, konstans- és művelethalmazt jelöl.
Adat szempontjából írja le a rendszer statikus tulajdonságait (osztály vagy entitás-relációs diagram)
A működés időbeliségét rögzíti (állapotgráf, kommunikációs diagram).
Funkció szerint ír le (használati esetek).
http://www.tankonyvtar.hu/hu/tartalom/tkt/objektum-orientalt/ch03.html
*
: Egy ember 0 vagy több kutyának lehet gazdája.0..1
: Egy kutyának legfeljebb egy gazdája van, de lehet, hogy gazdátlan.1..*
).1..*
: Egy hallgató több tárgyból is vizsgázhat.1..*
: Egy tárgyból több hallgató is vizsgázhat.class Ember {
String nev;
Date szulDatum;
public:
Ember();
void setDate(Date d);
void setName(char *n);
const char *getName() const;
// ...
};
class Diak : public Ember {
// ^Alaposztályból minden
// látszik, ami publikus
double atlag;
// ^ +új attribútum
public:
Diak();
void setAv(double a);
// ^ +új tagfüggvény
};
class Tanar : public Ember {
double fizetes;
public:
Tanar(); // ...
};
class Alakzat {
protected:
// ^ Védelem enyhítése a leszármazottak felé
int x, y;
int szin;
public:
Alakzat(int x0, int y0, int sz) : x(x0), y(y0), szin(sz) {}
// mozgat(), érezzük, hogy itt a helye, de nem
// tudjuk hogyan kell megvalósítani a rajzolás részét.
// Ezért oda tesszük, ahol már ismert a rajzolás menete.
};
class Szakasz : public Alakzat {
// ^ Alaposztályból minden látszik ami publikus
int xv, yv;
public:
Szakasz(int x1, int y1, int x2, int y2, int sz)
: Alakzat(x1, y1, sz), xv(x2), yv(y2) {}
void rajzol();
void mozgat(int dx, int dy);
// ^ Itt már tudjuk, hogyan kell rajzolni.
};
void Szakasz::rajzol() {
// ... szakaszt rajzol
}
void Szakasz::mozgat(int dx, int dy) {
int sz = szin; // tényleges rajzolási szín elmentése
szin = BACKGND; // rajzolási szín legyen a háttér színe
rajzol(); // A vonal letörlése az eredeti helyről
x += dx;
y += dy; // mozgatás: a pozíció változik
szin = sz; // rajzolási szín a tényleges szín
rajzol(); // A vonal felrajzolása az új pozícióra
}
class Teglalap : public Alakzat {
int xc, yc;
public:
Teglalap(int x1, int y1, int x2, int y2, int sz)
: Alakzat(x1, y1, sz), xc(x2), yc(y2) {}
void rajzol();
void mozgat(int dx, int dy);
// ^ Ugyanaz, mint a szakasznál, csak a hívott rajzol() más
};
rajzol()
másrajzol()
-t egy manó le tudná cserélni mindig a megfelelő származtatott rajzol()
-ra, akkor működne → virtuális függvényclass Alakzat {
protected:
int x, y;
int szin;
public:
Alakzat(int x0, int y0, int sz) : x(x0), y(y0), szin(sz) {}
virtual void rajzol() {}
// ^ Az öröklés során újabb jelentést kaphat, ami az
// alaposztályból is elérhető, így a mozgat()-ból is.
void mozgat(int dx, int dy);
// ^ Most már ide tehetjük, mert a rajzol() is itt van.
};
void Alakzat::mozgat(int dx, int dy) {
int sz = szin; // tényleges rajzolási szín elmentése
szin = BACKGRD; // rajzolási szín legyen a háttér színe
rajzol(); // A vonal letörlés az eredeti helyről
x += dx;
y += dy; // mozgatás: a pozíció változik
szin = sz; // rajzolási szín a tényleges szín
rajzol(); // A vonal felrajzolása az új pozícióra
}
class Szakasz : public Alakzat {
int xv, yv;
public:
Szakasz(int x1, int y1, int x2, int y2, int sz)
: Alakzat(x1, y1, sz), xv(x2), yv(y2) {}
void rajzol(); // átdefiniáljuk a virt. fv-t.
// void mozgat(int dx, int dy);
};
void Szakasz::rajzol() {
// ... szakaszt rajzol.
// Az alaposztályból hívva is ez hívódik
}
class Teglalap : public Alakzat {
int xc, yc;
public:
Teglalap(int x1, int y1, int x2, int y2, int sz)
: Alakzat(x1, y1, sz), xc(x2), yc(y2) {}
void rajzol(); // átdefiniáljuk a virt. fv-t.
// void mozgat(int dx, int dy);
};
void Teglalap::rajzol() {
// ... téglalapot rajzol.
// Az alaposztályból hívva is ez hívódik
}
int main() {
Teglalap tegla(1, 10, 2, 40, RED); // téglalap
Szakasz szak(3, 6, 80, 40, BLUE); // szakasz
Alakzat alak(3, 4, GREEN); // ???
alak.mozgat(3, 4); // 2 db rajzol() hívás
szak.rajzol(); // 1 db rajzol()
szak.mozgat(10, 10); // 2 db rajzol() hívás
Alakzat *ap[10];
ap[0] = &szak; // nem kell típuskonverzió
ap[1] = &tegla; // kompatibilis
for (int i = 0; i < 2; i++) {
ap[i]->rajzol();
}
}
Virtuális | Nem virtuális | |
---|---|---|
Alakzat::rajzol() | Alakzat::rajzol() | |
alak.mozgat() | Alakzat::rajzol() | Alakzat::rajzol() |
szak.rajzol() | Szakasz::rajzol() | Szakasz:rajzol() |
szak.mozgat | Szakasz::rajzol() | Alakzat::rajzol() |
sp[0]->rajzol() Szakasz-ra mutat | Szakasz::rajzol() | Alakzat::rajzol() |
sp[1]->rajzol() Teglalap-ra mutat | Teglalap::rajzol() | Alakzat::rajzol() |
class Alakzat {
protected:
int x, y, szin;
public:
Alakzat(int x0, int y0, int sz);
void mozgat(int dx, int dy);
virtual void rajzol() = 0; // tisztán virtuális
// ^ Nem példányosítható
virtual ~Alakzat(){}; // Ez meg mi ?
};
struct A {
~A(){};
};
class B : public A {
char *p;
public:
B() { p = new char[10]; }
~B() { delete[] p; }
};
int main() {
A *pa = new B;
delete pa;
} // ~B() nem hívódik meg, memleak p-nél
// Direct leak of 10 byte(s) in 1 object(s) allocated from:
// #0 0x4f5512 in operator new[](unsigned long)
// #1 0x4f8300 in B::B()
struct A {
virtual ~A(){};
// ^ Virtuális destruktor
};
class B : public A {
char *p;
public:
B() { p = new char[10]; }
~B() { delete[] p; }
};
int main() {
A *pa = new B;
delete pa;
} // A lefoglalt terület felszabadult, mert a
// származtatott osztály destruktora is lefutott
https://git.ik.bme.hu/Prog2/eloadas_peldak/ea_05 virt_destruktor és otthonMegMukodott
struct Alakzat {
int x, y, szin;
void (*rajzol)();
};
void AlakzatRajzol(struct Alakzat *this) {}
void AlakzatKonstr(struct Alakzat *this, int x0, int y0, int sz) {
this->rajzol = AlakzatRajzol; // manó v. fordító ?
this->x = x0;
this->y = y0;
this->szin = sz;
}
void AlakzatMozgat(struct Alakzat *this, int dx, int dy) {
int sz = this->szin;
this->szin = BACKGND;
(*(this->rajzol))(this); //
this->x += dx;
this->y += dy;
this->szin = sz;
(*(this->rajzol))(this); //
}
class Teglalap : public Alakzat {
int xc, yc;
public:
Teglalap(int x1, int y1, int x2, int y2, int sz)
: Alakzat(x1, y1, sz), xc(x2), yc(y2) {}
void ujMeret(int x2, int y2) {
xc = x + x2;
yc = y + y2;
}
void rajzol();
// mozgat() az alaposztályban
};
class Negyzet : private Teglalap {
// Eltakarja az alaposztályt ^
public:
Negyzet(int x1, int y1, int s, int sz)
: Teglalap(x1, y1, x1 + s, y1 + s, sz) {}
void rajzol() { Teglalap::rajzol(); }
void mozgat(int dx, int dy) { Teglalap::mozgat(dx, dy); }
};
Az ujMeret()
fv-t így kívülről elérhetetlenné tettük (korlátoztuk az elérését)
attr\honnan | külső | származtatott | tagfüggvény és barát |
---|---|---|---|
public: | √ | √ | √ |
protected: | - | √ | √ |
private: | - | - | √ |
class Alakzat {
protected:
Pont p0; // alakzat origója
Szin sz; // alakzat színe
static SDL_Surface *scr; // eldugott ”globális” (SDL1)
public:
Alakzat(const Pont &p0, const Szin &sz) : p0(p0), sz(sz) {}
const Pont &getp0() const { return p0; }
static void setSurface(SDL_Surface *s) { scr = s; }
virtual void rajzol() const = 0; // tisztán virt.
void mozgat(const Pont &d);
virtual ~Alakzat() {} // fontos, ha az alap. felől szabadítunk fel
};
class Kor : public Alakzat {
int r; //< sugár
public:
Kor(const Pont &p0, int r, Szin sz)
: Alakzat(p0, sz), r(r) // Ősosztály inic
{}
void rajzol() const;
// Miért nincs destruktor?
};
void Kor::rajzol() const {
filledCircleColor(scr, p0.x, p0.y, r, sz);
SDL_Flip(scr); // képfrissítés
}
Alakzat *idom[100]; unsigned int db = 0;
while (VAN_ESEMENY) {
if (GOMBNYOMAS) {
if (db < 100 - 1) {
idom[db] = new Kor(Pont(x, y), 40, RED); // felvesz
idom[db]->rajzol(); ++db;
}
} else if (EGERMOZGAS) {
for (int i = 0; i < db; i++) {
idom[i]->mozgat(Pont(dx, dy)); idom[i]->rajzol();
}
}
}
for (int i = 0; i < db; i++) { delete idom[i]; } // letöröl