function overriding (tsz. function overridings)
A metódus felüldefiniálás azt jelenti, hogy egy származtatott (leszármazott) osztályban egy már létező függvényt újraimplementálunk.
Feltételek: 1. Ugyanolyan névvel kell rendelkeznie, mint a szülőosztály függvénye. 2. Ugyanolyan paraméterlistával kell rendelkeznie (különben metódus túlterhelés történik, nem felüldefiniálás). 3. A szülőosztályban a függvénynek virtual
-ként kell szerepelnie, hogy dinamikus kötést lehessen alkalmazni.
#include <iostream>
using namespace std;
class Alap {
public:
virtual void koszont() { // A `virtual` lehetővé teszi a felüldefiniálást
cout << "Üdvözöllek az Alap osztályból!" << endl;
}
};
class Szarmaztatott : public Alap {
public:
void koszont() override { // Felüldefiniálás
cout << "Helló a Származtatott osztályból!" << endl;
}
};
int main() {
Alap* obj = new Szarmaztatott(); // Alaposztály pointere mutat a leszármazott objektumra
obj->koszont(); // A leszármazott osztály metódusa hívódik meg (dinamikus kötés)
delete obj;
return 0;
}
Kimenet:
Helló a Származtatott osztályból!
Mi történik itt? 1. Az Alap osztályban van egy virtual void koszont()
függvény. 2. A Szarmaztatott osztályban ezt újraimplementáljuk. 3. A polimorfizmus miatt a megfelelő függvény fut le a futásidőben (runtime), nem pedig fordítási időben.
virtual
kulcsszó?Ha az Alap
osztályban nem lenne virtual
, akkor a statikus kötés miatt mindig az alaposztály metódusa hívódna meg, függetlenül attól, hogy valójában milyen típusú objektumot használunk.
Példa virtual
nélkül:
class Alap {
public:
void koszont() { // NINCS `virtual`
cout << "Üdvözöllek az Alap osztályból!" << endl;
}
};
class Szarmaztatott : public Alap {
public:
void koszont() {
cout << "Helló a Származtatott osztályból!" << endl;
}
};
int main() {
Alap* obj = new Szarmaztatott();
obj->koszont(); // A szülőosztály függvénye hívódik meg!
delete obj;
return 0;
}
Kimenet:
Üdvözöllek az Alap osztályból!
Mivel a koszont()
nincs virtual
-ként megjelölve, az alaposztály függvénye hívódik meg még akkor is, ha az objektum valójában a Szarmaztatott osztályból származik.
Ezért szükséges a virtual
használata, hogy a helyes metódus fusson le futásidőben.
override
és final
kulcsszavakC++11-től két kulcsszó is használható a felüldefiniálásnál:
override
– Segít az egyértelműségbenA override
kulcsszó jelzi, hogy egy függvény felülír egy szülőbeli metódust.
class Alap {
public:
virtual void koszont() {
cout << "Alap osztály" << endl;
}
};
class Szarmaztatott : public Alap {
public:
void koszont() override { // `override` jelzi, hogy ez felüldefiniálás
cout << "Szarmaztatott osztály" << endl;
}
};
Ha a override
kulcsszó jelen van, és rossz szignatúrával próbálunk felüldefiniálni egy metódust, akkor a fordító hibát jelez.
final
– Megakadályozza a további felüldefiniálástA final
kulcsszóval megtilthatjuk a további felüldefiniálást.
class Alap {
public:
virtual void koszont() final { // Ezt már nem lehet felüldefiniálni!
cout << "Alap osztály" << endl;
}
};
class Szarmaztatott : public Alap {
public:
// void koszont() override { // Fordítási hiba! Mert `final`
// cout << "Szarmaztatott osztály" << endl;
// }
};
Ha egy osztályt final
-ként jelölünk (class Szarmaztatott final
), akkor abból nem lehet leszármaztatni új osztályokat.
A konstruktorok nem lehetnek virtuálisak, viszont a destruktoroknak virtuálisnak kell lenniük az örökölt objektumok helyes törlése érdekében.
class Alap {
public:
virtual ~Alap() { // Virtuális destruktor biztosítja a helyes felszabadítást
cout << "Alap destruktor" << endl;
}
};
class Szarmaztatott : public Alap {
public:
~Szarmaztatott() {
cout << "Szarmaztatott destruktor" << endl;
}
};
int main() {
Alap* obj = new Szarmaztatott();
delete obj; // Helyesen mindkét destruktor lefut!
return 0;
}
Kimenet:
Szarmaztatott destruktor Alap destruktor
Ha a ~Alap()
nem lenne virtuális, akkor csak az alaposztály destruktora hívódna meg, és memóriaszivárgás történhetne.
Tulajdonság | Magyarázat |
---|---|
Felüldefiniálás (override )
|
A leszármazott osztály újraimplementálja a szülő metódusát. |
virtual kulcsszó
|
Az alaposztály metódusa virtuális kell legyen a helyes működéshez. |
Statikus vs. dinamikus kötés | Virtuális nélkül statikus kötés van (fordítási időben dől el), virtuálissal dinamikus kötés történik (futásidőben). |
override
|
Fordítási hibát ad, ha nincs pontos egyezés a szülő metódussal. |
final
|
Megakadályozza a további felüldefiniálást. |
Virtuális destruktor | Megakadályozza a memóriaszivárgást az öröklődő objektumok törlésekor. |
A metódus felüldefiniálás egy alapvető OOP technika C++-ban, amely lehetővé teszi a polimorfizmus és a dinamikus kötés használatát. Ha az öröklődést helyesen kezeljük virtuális függvényekkel és destruktorokkal, akkor rugalmasabb és karbantarthatóbb kódot kapunk.