Öröklés, heterogén kollekció
Dobra Gábor · 2022.03.10.
Öröklés, heterogén kollekció
A videó a jegyzet 7. fejezetéhez tartozik.
A kész példaprogram innen letölthető.
#include <iostream>
#include <string>
#include <vector>
int main() {
std::string str = "hello";
str[0] = toupper(str[0]);
str.push_back('!');
std::cout << str.size() << " " << str << std::endl;
std::vector<int> t = {1, 2, 3};
t.push_back(4);
for (size_t i = 0; i < t.size(); ++i)
std::cout << t[i] << " ";
}
- a "minden micsoda micsoda" reláció, is-a
- ősosztály, leszármazott
- Liskov Substitution Principle (LSP): minden leszármazottnak teljesítenie kell mindent, amit az ősosztály ígér!
- a leszármazott örökli az ősosztály minden tagfüggvényét
class Alakzat {
uint32_t szin;
public:
// rajzolás?
};
class Teglalap : public Alakzat {
// ...
};
- mindenhova, ahova ősosztály példánya kerül, kerülhet leszármazott
Teglalap& → Alakzat&
, ésTeglalap* → Alakzat*
implicit
void invertal(Alakzat& alakzat) {
uint32_t regiszin = alakzat.get_szin();
uint32_t ujszin = szin_invertal(regiszin);
alakzat.set_szin(ujszin);
}
int main() {
Teglalap t1(/* ... */);
invertal(t1); // Teglalap& → Alakzat&
}
- a függvényben a statikus típus
Alakzat
, a dinamikus típusTeglalap
#include <iostream>
class Alakzat {
public:
virtual void kiir() const {
std::cout << "Alakzat::kiir()" << std::endl;
}
};
class Kor : public Alakzat {
public:
virtual void kiir() const {
std::cout << "Kor::kiir()" << std::endl;
}
};
void kiir(Alakzat const& a) {
a.kiir();
}
int main() {
Kor kor;
kor.kiir();
kiir(kor);
}
Ősosztály függvényének az explicit hívása:
class Kor : public Alakzat {
public:
virtual void kiir() const {
Alakzat::kiir(); // !
std::cout << "Kor::kiir()" << std::endl;
}
};
- az ősosztályban kell virtuálissá tennünk a függvényeket
- a leszármazottban pontosan azt kell override-olni, amit az ősosztály kér
#include <iostream>
class Alakzat {
public:
virtual void kiir() const {
std::cout << "Alakzat::kiir()" << std::endl;
}
};
class Kor : public Alakzat {
public:
void kiir() const override { // !
std::cout << "Kor::kiir()" << std::endl;
}
};
class Alakzat {
uint32_t szin;
public:
explicit Alakzat(uint32_t szin) : szin(szin) {}
/* ... */
};
class Kor : public Alakzat {
Pont kozeppont;
int sugar;
public:
Kor(uint32_t szin, Pont kozeppont, int sugar)
: Alakzat(szin) // ősosztály konstruktora
, kozeppont(kozeppont)
, sugar(sugar) {
}
/* ... */
};
Sorrendtartó kirajzolás:
Teglalap t1(0xFF0000FF, Pont(1, 2), 4, -4);
Kor k1(0x00FF00FF, Pont(3, 0), 2);
std::vector<Alakzat*> alakzatok; // közös tároló
alakzatok.push_back(&t1); // Teglalap* -> Alakzat*
alakzatok.push_back(&k1); // Kor* -> Alakzat*
for (size_t i = 0; i < alakzatok.size(); ++i)
alakzatok[i]->rajzol(); // a kör a téglalap felett van
class Alakzat {
uint32_t szin;
public:
virtual void rajzol() const = 0;
virtual bool bennevan(Pont p) const = 0;
virtual void mozgat(Pont ennyivel) = 0;
virtual ~Alakzat() {} // !
};
class Kor : public Alakzat {
Pont kozeppont;
int sugar;
public:
// default destruktor jó
};
- ősosztályban kell virtuálissá tenni a destruktort
- általában: ha van legalább egy virtuális függvény
std::vector<Alakzat*> alakzatok; // tulajdonos: majd törölnie kell!
alakzatok.push_back(new Teglalap(0xFF0000FF, Pont(1, 2), 4, 3));
alakzatok.push_back(new Kor(0x00FF00FF, Pont(3, 0), 2));
for (size_t i = 0; i < alakzatok.size(); ++i)
alakzatok[i]->rajzol();
for (size_t i = 0; i < alakzatok.size(); ++i)
delete alakzatok[i]; // destruktort hív
- mindig pointert tárol
- dinamikus memóriát kezel
std::vector<Alakzat*> alakzatok;
Alakzat* mozgatott = NULL;
while (SDL_WaitEvent(&ev) && ev.type != SDL_QUIT) {
switch (ev.type) {
case SDL_MOUSEBUTTONDOWN:
Pont hol(ev.button.x, ev.button.y);
for (int i = alakzatok.size() - 1; i >= 0; --i) {
if (alakzatok[i]->bennevan(hol)) { // bennevan()
mozgatott = alakzatok[i];
break; /* első után álljunk meg */
}
}
break;
case SDL_MOUSEBUTTONUP:
mozgatott = NULL;
break;
/* ... */
}
}
std::vector<Alakzat*> alakzatok;
Alakzat* mozgatott = NULL;
while (SDL_WaitEvent(&ev) && ev.type != SDL_QUIT) {
bool mozgott = false;
switch (ev.type) {
/* ... */
case SDL_MOUSEMOTION:
Pont mennyivel(ev.motion.xrel, ev.motion.yrel);
if (mozgatott != NULL) {
mozgatott->mozgat(mennyivel); // mozgat()
mozgott = true;
}
break;
}
if (mozgott) {
/* ... háttér ... */
for (size_t i = 0; i < alakzatok.size(); ++i)
alakzatok[i]->rajzol(renderer); // rajzol()
}
}
class Alakzat {
/* ... */
public:
virtual void rajzol() const = 0;
virtual bool bennevan(Pont p) const = 0;
virtual void mozgat(Pont ennyivel) = 0;
virtual ~Alakzat() {}
};
class Kor : public Alakzat {
/* ... */
public:
void rajzol() const override {
SDL_rajzol_kor(kozeppont.x, kozeppont.y, sugar, szin);
}
void bool bennevan(Pont p) const override {
return tavolsag(p, kozeppont) <= sugar;
}
void mozgat(Pont ennyivel) override {
kozeppont += ennyivel;
}
};
Az Alakzat egész interfésze polimorf.
class Alakzat {
uint32_t szin;
public:
uint32_t get_szin() const;
void set_szin(uint32_t uj);
virtual void rajzol() const = 0;
virtual bool bennevan(Pont p) const = 0;
virtual void mozgat(Pont ennyivel) = 0;
virtual ~Alakzat();
Alakzat* kattint(Pont p) { // nem virtuális, de polimorf!
if (bennevan(p))
return this;
return nullptr;
}
};
- a main-t nem érdekli a szín, egyáltalán
- elképzelhető olyan leszármazott, aminek nincs (egy, kitüntetett) színe
class Alakzat { // "interface"
public:
virtual void rajzol() const = 0;
virtual bool bennevan(Pont p) const = 0;
virtual void mozgat(Pont ennyivel) = 0;
virtual ~Alakzat() {}
};
class TeliAlakzat : public Alakzat { // ez is absztrakt!
uint32_t szin;
public:
uint32_t get_szin() const;
void set_szin(uint32_t uj);
};
class Alakzat { /* ... */ };
class TeliAlakzat : public Alakzat { /* ... */ };
class Teglalap : public TeliAlakzat {
int a;
int b;
/* ... */
};
class Negyzet : public TeliAlakzat {
int a;
/* ... */
};
A kész példaprogram innen letölthető.