Achtergrond van de vraag:
In C++ werd ondersteuning voor object-georiënteerd programmeren geïntroduceerd, wat essentieel is voor moderne talen. Om polymorfisme te implementeren, werden virtuele functies gebruikt. Dit maakte het mogelijk om de juiste implementatie van een methode tijdens runtime aan te roepen in plaats van alleen tijdens compilatie, wat cruciaal is voor een architectuur met overerving.
Probleem:
Een veelvoorkomende vergissing is de verwarring tussen statische en dynamische aanroepingen van methoden, vergeten virtuele destructors, en onjuiste omgang met overerving (bijvoorbeeld, object slicing, aanroep van de base versie in plaats van override). Vaak wordt verwarrend hoe polymorfisme daadwerkelijk werkt.
Oplossing:
Een virtuele functie wordt gedeclareerd met het sleutelwoord virtual in de basisklasse en kan worden overschreven (override) in de afgeleide klasse. Als een functie wordt aangeroepen via een pointer of referentie op de basisklasse, wordt de versie van de afgeleide klasse uitgevoerd.
Voorbeeld code:
struct Base { virtual void foo() { std::cout << "Base::foo\n"; } }; struct Derived : Base { void foo() override { std::cout << "Derived::foo\n"; } }; void call(Base& b) { b.foo(); } int main() { Derived d; call(d); // Toont Derived::foo }
Belangrijkste kenmerken:
Werkt polymorfisme bij het doorgeven van een object per waarde?
Nee. Doorgeven per waarde leidt tot "slicing" — alleen de relevantie deel, overeenkomstig het parameter type (meestal de basisklasse), wordt gekopieerd, en polymorfisme wordt uitgeschakeld.
Voorbeeld code:
void call(Base b) { b.foo(); } // altijd aanroep van Base::foo
Moet een destructor als virtueel worden gedeclareerd in de basisklasse?
Ja, als het de bedoeling is om afgeleide objecten te verwijderen via een pointer naar de basisklasse. Anders leidt dit tot geheugenlekken of niet afgesloten bronnen.
Voorbeeld code:
struct Base { virtual ~Base() {} };
Wat gebeurt er als het sleutelwoord override niet wordt gebruikt in de afgeleide klasse?
Als in de afgeleide klasse override niet wordt opgegeven, maar per ongeluk de handtekening van de functie wordt gewijzigd (zoals het vergeten van const of fouten in de parameters), wordt de functie niet overschreven, maar wordt er een nieuwe aangemaakt, en werkt polymorfisme niet zoals verwacht.
De programmeur heeft de destructor van de basisklasse niet als virtueel gedeclareerd; het verwijderen van een array van objecten via een basispointer leidde tot een geheugenlek.
Voordelen:
Nadelen:
Er was een virtuele destructor gedeclareerd; alleen referenties/pointers naar het basistype werden gebruikt. Polymorfisme werkte correct.
Voordelen:
Nadelen: