ProgrammatieC++ ontwikkelaar

Hoe werkt het mechanisme van virtuele functies en virtueel erven in C++? Welke specificaties zijn er bij het ontwerpen van klassen met een abstracte interface?

Slaag voor sollicitatiegesprekken met de Hintsage AI-assistent

Antwoord

Virtuele functies maken polymorfisme mogelijk — de mogelijkheid om objecten van afgeleide klassen te verwerken via pointers of referenties naar de basis klasse. Om een virtuele functie te declareren, gebruik je het sleutelwoord virtual:

class Base { public: virtual void foo() { std::cout << "Base::foo"; } }; class Derived : public Base { public: void foo() override { std::cout << "Derived::foo"; } };

Bij het aanroepen van foo() via een pointer van type Base* wordt de implementatie in Derived aangeroepen, als deze wijst naar een object van Derived.

Abstracte klasse — bevat ten minste één puur virtuele functie:

class Interface { public: virtual void process() = 0; // puur virtuele functie };

Virtueel erven lost het probleem van diamantvererving (diamond problem) op:

class A { }; class B : virtual public A { }; class C : virtual public A { }; class D : public B, public C { };

Dit garandeert dat er in object D slechts één instantie van de basis klasse A zal zijn.

Vraag met een valkuil

Wat is het verschil tussen een virtuele destructor en een gewone? Moet je een destructor virtueel maken als de klasse bedoeld is voor erfenis?

Antwoord: Een virtuele destructor garandeert dat bij verwijdering via een pointer van het basistype de destructor van de afgeleide klasse zelf wordt aangeroepen. Dit is belangrijk voor een correcte vrijgave van middelen.

class Base { public: virtual ~Base() {} };

Als de destructor niet virtueel is, dan zal bij delete BasePtr; bij het object van de afgeleide klasse alleen de destructor van Base worden aangeroepen, en zullen de middelen van de geërfde velden niet worden vrijgegeven.

Voorbeelden van werkelijke fouten door onbekendheid met de nuances van het onderwerp


Verhaal

In een groot financieel systeem, waar voor verschillende instrumenten een gemeenschappelijke interface van de klasse was, werd de virtuele destructor niet gedeclareerd. In een van de afgeleiden werd een dynamische bron gebruikt. Bij het verwijderen van het object via een pointer naar het basistype trad er geheugenlek op, dat pas in productie werd ontdekt.


Verhaal

Het team gebruikte meervoudige erfenis zonder virtueel erven. Klasse D erfde A twee keer via tussenliggende klassen. Dit leidde tot duplicatie van staat en fouten bij het aanspreken van leden van A. Het werd pas gecorrigeerd na een audit met behulp van statische analysetools.


Verhaal

In een project voor de ontwikkeling van logging plugins werd een abstracte klasse gebruikt, maar werd de destructor niet virtueel gemaakt. Bij het verwijderen van plugins via een pointer naar de interface werden niet-voor-de-hand-liggende afhankelijkheden en bugs waargenomen, gerelateerd aan niet-aangeroepen destructors van afgeleiden. Het probleem omvatte de pool van middelen en leidde tot een bronlek.