C++ Polymorfisme met voorbeeld

โšก Slimme samenvatting

Polymorfisme in C++ Hiermee kan รฉรฉn enkele lidfunctie vele vormen aannemen en zich anders gedragen afhankelijk van het object dat deze aanroept. Het is gebaseerd op overerving en komt in twee varianten voor, die ofwel tijdens het compileren ofwel tijdens de uitvoering worden bepaald.

  • ๐Ÿ”ท Definitie: Polymorfisme betekent "vele vormen", waardoor een lidfunctie zich anders kan gedragen afhankelijk van het object dat de functie aanroept.
  • ๐Ÿงญ Twee types: C++ Biedt polymorfisme tijdens het compileren (statisch) en polymorfisme tijdens de uitvoering (dynamisch).
  • ๐Ÿงฉ Compilatietijd: Functie-overloading en operator-overloading selecteren de juiste functie tijdens de compilatie.
  • ๐Ÿ” Looptijd: Functie-overriding en virtuele functies zorgen ervoor dat de aanroep tijdens de uitvoering van het programma wordt afgehandeld.
  • โš™๏ธ Virtuele functies: Het sleutelwoord 'virtual' in een basisklasse maakt late binding mogelijk via een pointer naar de basisklasse.
  • ๐Ÿค– AI-assistentie: GitHub Copilot en vergelijkbare hulpprogramma's genereren overbelaste, overschreven en virtuele functies op basis van een korte opmerking.

C++ polymorfisme

Waar zit polymorfisme in? C++?

In C++Polymorfisme zorgt ervoor dat een lidfunctie zich anders gedraagt, afhankelijk van het object dat deze aanroept. Polymorfisme is een Grieks woord dat 'veel vormen hebben' betekent. Het treedt op wanneer je een hiรซrarchie hebt van klassen verwant door erfelijkheid.

Stel dat we bijvoorbeeld de functie makeSound() hebben. Wanneer een kat deze functie oproept, produceert hij het miauwgeluid. Wanneer een koe dezelfde functie aanroept, zal zij het miauwgeluid voortbrengen.

Polymorfisme in C++ voorbeeld van geluid maken

Hoewel we รฉรฉn functie hebben, gedraagt โ€‹โ€‹deze zich onder verschillende omstandigheden anders. De functie kent vele vormen; daarom hebben we polymorfisme bereikt.

Nu het kernidee vaststaat, C++ verdeelt dit gedrag in twee afzonderlijke categorieรซn.

Soorten polymorfisme

C++ ondersteunt twee soorten polymorfisme:

  • Compile-time polymorfisme, en
  • Runtime-polymorfisme.

Soorten polymorfisme in C++

De eerste categorie verwerkt elke functieaanroep voordat het programma รผberhaupt wordt uitgevoerd.

Compileer tijdpolymorfisme

U roept de overbelaste functies aan door het aantal en het type argumenten te matchen. De informatie is aanwezig tijdens het compileren. Dit betekent de C++ compiler selecteert de juiste functie tijdens het compileren.

Polymorfisme tijdens compilatietijd wordt bereikt door functieoverbelasting en operatoroverbelasting.

Functie Overbelasting

Overbelasting van functies treedt op als we veel functies hebben met vergelijkbare namen maar met verschillende argumenten. De argumenten kunnen verschillen qua aantal of type.

Voorbeeld 1

#include <iostream> 
using namespace std;

void test(int i) {
	cout << " The int is " << i << endl;
}
void test(double  f) {
	cout << " The float is " << f << endl;
}
void test(char const *ch) {
	cout << " The char* is " << ch << endl;
}

int main() {
	test(5);
	test(5.5);
	test("five");
	return 0;
}

Output:

C++ Functie-overloading Voorbeeld 1 uitvoer

Hier is een screenshot van de code:

C++ Functie-overloading Voorbeeld 1 code

Code Uitleg:

  1. Neem het iostream-headerbestand op in onze code. We zullen de functies ervan kunnen gebruiken.
  2. Neem de std-naamruimte op in onze code. We kunnen de klassen ervan gebruiken zonder deze aan te roepen.
  3. Maak een functie met de naam test waaraan een geheel getalparameter i moet doorgegeven worden. De { markeert het begin van de hoofdtekst van de functietest.
  4. Verklaring die moet worden uitgevoerd als de bovenstaande functietest wordt aangeroepen/aangeroepen.
  5. Einde van de hoofdtekst van bovenstaande functietest.
  6. Maak een functie met de naam test waaraan een float-parameter f moet doorgegeven worden. De { markeert het begin van de hoofdtekst van de functietest.
  7. Verklaring die moet worden uitgevoerd als de bovenstaande functietest wordt aangeroepen/aangeroepen.
  8. Einde van de hoofdtekst van de bovenstaande functietest.
  9. Maak een functie met de naam test waaraan een tekenparameter ch moet doorgegeven worden. De { markeert het begin van de hoofdtekst van de functietest.
  10. Verklaring die moet worden uitgevoerd als de bovenstaande functietest wordt aangeroepen/aangeroepen.
  11. Einde van de hoofdtekst van de bovenstaande functietest.
  12. Roep de functie main() aan. De { markeert het begin van de hoofdtekst van de functie.
  13. Roep de functie test op en geef er 5 aan als de waarde van het argument. Hierdoor wordt de testfunctie aangeroepen die een geheel getal-argument accepteert, dat wil zeggen de eerste testfunctie.
  14. Roep de functie test aan en geef er 5.5 aan als de waarde van het argument. Hierdoor wordt de testfunctie aangeroepen die een float-argument accepteert, dat wil zeggen de tweede testfunctie.
  15. Roep de functie test op en geef er vijf aan als de waarde van het argument. Hierdoor wordt de testfunctie aangeroepen die een karakterargument accepteert, dat wil zeggen de derde testfunctie.
  16. Het programma moet een waarde retourneren als het succesvol wordt uitgevoerd.
  17. Het einde van de hoofdtekst van de functie main().

We hebben drie functies met dezelfde naam, maar verschillende soorten argumenten. We hebben polymorfisme bereikt.

Operavoor overbelasting

In OperaVoor Overbelasting definiรซren we een nieuwe betekenis voor a C++ operatorHet verandert ook de werking van de operator. We kunnen bijvoorbeeld de operator + definiรซren om twee tekenreeksen samen te voegen. We kennen hem als de opteloperator voor het optellen van getallen. Na onze definitie zal hij, wanneer hij tussen gehele getallen wordt geplaatst, deze optellen. Wanneer hij tussen tekenreeksen wordt geplaatst, zal hij deze samenvoegen.

Voorbeeld 2

#include<iostream> 
using namespace std;

class ComplexNum {
private:
	int real, over;
public:
	ComplexNum(int rl = 0, int ov = 0) {
		real = rl;   
		over = ov; 
	}

	ComplexNum operator + (ComplexNum const &obj) {
		ComplexNum result;
		result.real = real + obj.real;
		result.over = over + obj.over;
		return result;
	}
	void print() { 
		cout << real << " + i" << over << endl; 
	}
};
int main()
{
	ComplexNum c1(10, 2), c2(3, 7);
	ComplexNum c3 = c1+c2;
	c3.print();
}

Output:

C++ Operatoroverbelasting Voorbeeld 2 uitvoer

Hier is een screenshot van de code:

C++ Operatoroverbelasting Voorbeeld 2 code

C++ Operatoroverbelasting Voorbeeld 2 resultaat

Code Uitleg:

  1. Voeg het iostream-headerbestand toe aan ons programma om de functies ervan te kunnen gebruiken.
  2. Neem de std-naamruimte op in ons programma om de klassen ervan te gebruiken zonder deze aan te roepen.
  3. Maak een klasse genaamd ComplexNum. De { markeert het begin van de klassebody.
  4. Gebruik de private access modifier om variabelen als privรฉ te markeren, wat betekent dat ze alleen toegankelijk zijn vanuit de klasse.
  5. Definieer twee integer-variabelen, real en over.
  6. Gebruik de toegangsaanpasser `public` om de constructor als openbaar te markeren, zodat deze ook van buiten de klasse toegankelijk is.
  7. Maak de klassenconstructor en initialiseer de variabelen.
  8. Initialiseer de waarde van de variabele real.
  9. Initialiseer de waarde van de variabele over.
  10. Einde van het constructorlichaam.
  11. We moeten de betekenis van de + operator overschrijven.
  12. Maak het gegevenstyperesultaat van het type ComplexNum.
  13. Gebruik de + operator met complexe getallen. Deze regel telt het reรซle deel van een getal op bij het reรซle deel van een ander getal.
  14. Gebruik de + operator met complexe getallen. Deze regel telt het imaginaire deel van een getal op bij het imaginaire deel van een ander getal.
  15. Het programma retourneert de waarde van het variabele resultaat bij succesvolle uitvoering.
  16. Einde van de definitie van de nieuwe betekenis van de + operator, namelijk overbelasting.
  17. Roep de methode print() aan.
  18. Druk het nieuwe complexe getal af na optelling op de console.
  19. Einde van de hoofdtekst van de functie print().
  20. Einde van de hoofdtekst van de klasse ComplexNum.
  21. Roep de functie main() aan.
  22. Geef de waarden van zowel de reรซle als de complexe delen door die moeten worden opgeteld. Het eerste deel van c1 wordt opgeteld bij het eerste deel van c2, dat wil zeggen 10+3. Het tweede deel van c1 wordt opgeteld bij het tweede deel van c, dat wil zeggen 2+7.
  23. Voer een bewerking uit met behulp van de overbelaste + operator en sla het resultaat op in variabele c3.
  24. Druk de waarde van variabele c3 af op de console.
  25. Einde van de hoofdtekst van de functie main().

Waar polymorfisme tijdens het compileren al vroeg een beslissing neemt, stelt de tweede categorie de keuze uit tot na de uitvoering.

Runtime polymorfisme

Dit gebeurt wanneer de methode van een object wordt aangeroepen/aangeroepen tijdens runtime in plaats van tijdens het compileren. Runtime-polymorfisme wordt bereikt door het overschrijven van functies. De aan te roepen/aanroepende functie wordt tijdens runtime tot stand gebracht.

Functie overschrijven

Het overschrijven van functies vindt plaats wanneer een functie van de basisklasse een nieuwe definitie krijgt in een afgeleide klasse. Op dat moment kunnen we zeggen dat de basisfunctie is overschreven.

Bijvoorbeeld:

#include <iostream>
using namespace std;
class Mammal {

public:
	void eat() {

		cout << "Mammals eat...";
	}

};

class Cow: public Mammal {

public:
	void eat() {

		cout << "Cows eat grass...";
	}
};
int main(void) {

	Cow c = Cow();

	c.eat();

	return 0;

}

Output:

C++ functie overschrijft uitvoer

Hier is een screenshot van de code:

C++ functie-overschrijvende code

Code Uitleg:

  1. Importeer het iostream-headerbestand in ons programma om de functies ervan te gebruiken.
  2. Neem de std-naamruimte op in ons programma om de klassen ervan te gebruiken zonder deze aan te roepen.
  3. Maak een klasse met de naam Mammal. De { markeert het begin van de hoofdtekst van de klasse.
  4. Gebruik de modifier voor openbare toegang om de functie die we gaan maken in te stellen als openbaar toegankelijk. Het is toegankelijk van buiten deze les.
  5. Creรซer een publieke functie met de naam eten. De { markeert het begin van de functietekst.
  6. Druk de instructie af die aan de cout-functie is toegevoegd wanneer de functie eat() wordt aangeroepen.
  7. Het einde van de hoofdtekst van de functie eat().
  8. Einde van het lichaam van de klasse Zoogdier.
  9. Maak een klasse met de naam Cow die de klasse Mammal erft. Koe is de afgeleide klasse, terwijl Zoogdier de basisklasse is. De { markeert het begin van deze klasse.
  10. Gebruik de modifier voor openbare toegang om de functie die we gaan maken als openbaar toegankelijk te markeren. Het is toegankelijk van buiten deze les.
  11. Overschrijf de functie eat() die in de basisklasse is gedefinieerd. De { markeert het begin van de functietekst.
  12. De instructie die op de console moet worden afgedrukt wanneer deze functie wordt aangeroepen.
  13. Einde van de hoofdtekst van de functie eat().
  14. Einde van het lichaam van de klasse Koe.
  15. Roep de functie main() aan. De { markeert het begin van de hoofdtekst van deze functie.
  16. Maak een instantie van de klasse Cow en geef deze de naam c.
  17. Roep de functie eat() aan die is gedefinieerd in de klasse Cow.
  18. Het programma moet bij succesvolle voltooiing een waarde retourneren.
  19. Einde van de functie main().

C++ Virtuele functie

Een virtuele functie is een andere manier om runtime-polymorfisme te implementeren in C++. Het is een speciale functie die is gedefinieerd in een basisklasse en opnieuw is gedefinieerd in de afgeleide klasse. Om een โ€‹โ€‹virtuele functie te declareren, moet u het virtuele sleutelwoord gebruiken. Het sleutelwoord moet voorafgaan aan de declaratie van de functie in de basisklasse.

Als een virtuele functieklasse wordt geรซrfd, herdefinieert de virtuele klasse de virtuele functie om aan zijn behoeften te voldoen. Bijvoorbeeld:

#include <iostream>  
using namespace std;
class ClassA {
		public:
		virtual void show() {
			cout << "The show() function in base class invoked..." << endl;
		}
	};
	class ClassB :public ClassA {
	public:
		void show() 	{
			cout << "The show() function in derived class invoked...";
		}
	};
	int main() {
		ClassA* a;   
		ClassB b;
		a = &b;
		a->show();      
	}

Output:

C++ virtuele functie-uitvoer

Hier is een screenshot van de code:

C++ virtuele functiecode

Code Uitleg:

  1. Neem het iostream-headerbestand op in de code om de functies ervan te gebruiken.
  2. Neem de std-naamruimte op in onze code om de klassen ervan te gebruiken zonder deze aan te roepen.
  3. Maak een klasse met de naam ClassA.
  4. Gebruik de modifier voor openbare toegang om een โ€‹โ€‹klaslid als openbaar toegankelijk te markeren.
  5. Maak een virtuele functie met de naam show(). Het zal een publieke functie zijn.
  6. De tekst die moet worden afgedrukt wanneer de show() wordt aangeroepen, wordt aangeroepen. Het einde is een C++ trefwoord, wat eindregel betekent. Het verplaatst de muiscursor naar de volgende regel.
  7. Einde van de hoofdtekst van de virtuele functie show().
  8. Einde van het lichaam van de klasse Klasse A.
  9. Een nieuwe klasse maken met de naam ClassB die de klasse ClassA erft. KlasseA wordt de basisklasse, terwijl KlasseB de afgeleide klasse wordt.
  10. Gebruik de modifier voor openbare toegang om een โ€‹โ€‹klaslid als openbaar toegankelijk te markeren.
  11. Herdefinieer de virtuele functie show() die is afgeleid in de basisklasse.
  12. De tekst die op de console moet worden afgedrukt wanneer de functie show() die in de afgeleide klasse is gedefinieerd, wordt aangeroepen.
  13. Einde van de hoofdtekst van de functie show().
  14. Einde van de hoofdtekst van de afgeleide klasse, Klasse B.
  15. Roep de functie main() aan. De programmalogica moet in de hoofdtekst worden toegevoegd.
  16. Maak een pointervariabele met de naam a. Het verwijst naar de klasse genaamd ClassA.
  17. Maak een exemplaar van de klasse met de naam ClassB. De instantie krijgt de naam b.
  18. Wijs de waardenopslagplaatsen in het adres b toe in de variabele a.
  19. Roep de functie show() aan die is gedefinieerd in de afgeleide klasse. Er is een late binding geรฏmplementeerd.
  20. Einde van de hoofdtekst van de functie main().

Nu beide mechanismen behandeld zijn, worden ze in de onderstaande tabel naast elkaar vergeleken.

Compile-time polymorfisme versus. Runtime-polymorfisme

Dit zijn de belangrijkste verschillen tussen de twee:

Compilatietijd polymorfisme Runtime polymorfisme
Het wordt ook wel vroege binding of statisch polymorfisme genoemd. Het wordt ook wel late/dynamische binding of dynamisch polymorfisme genoemd.
De methode wordt aangeroepen/aangeroepen tijdens het compileren De methode wordt tijdens runtime aangeroepen/aangeroepen
Geรฏmplementeerd via functie-overbelasting en operator-overbelasting Geรฏmplementeerd via methodeoverschrijving en virtuele functies
Voorbeeld: overbelasting van de methode. Veel methoden kunnen vergelijkbare namen hebben, maar een verschillend aantal of verschillende soorten argumenten Voorbeeld: methode overschrijven. Veel methoden kunnen een vergelijkbare naam en hetzelfde prototype hebben.
Snellere uitvoering omdat de methodedetectie tijdens het compileren plaatsvindt. De uitvoering verloopt trager omdat de methodeontdekking tijdens de runtime plaatsvindt.
Less Er wordt flexibiliteit bij het oplossen van problemen geboden, omdat alles bekend is tijdens het compileren. Er is veel flexibiliteit voor het oplossen van complexe problemen, omdat methoden tijdens runtime worden ontdekt.

Veelgestelde vragen

Overloading definieert meerdere functies met dezelfde naam maar verschillende parameters, die tijdens het compileren worden opgelost zonder overerving. Overriding herdefinieert een overgeรซrfde functie van de basisklasse met dezelfde signatuur in een afgeleide klasse, die tijdens de uitvoering wordt opgelost wanneer de functie als virtueel wordt gedeclareerd.

Een zuiver virtuele functie wordt gedeclareerd met = 0 en heeft geen body in de basisklasse. Elke klasse die er een bevat, wordt een absolute virtuele functie.tract-klasse, die niet rechtstreeks geรฏnstantieerd kan worden en afgeleide klassen dwingt om hun eigen implementatie te leveren.

Wanneer een klasse virtuele functies declareert, bouwt de compiler een virtuele tabel (vtable) van functiepointers. Elk object slaat een verborgen pointer naar de vtable van de klasse op, zodat een pointer naar de basisklasse de juiste overschreven functie tijdens runtime aanroept via late binding.

Ja. Templates stellen รฉรฉn functie of klasse in staat om met meerdere gegevenstypen te werken, waarbij de compiler tijdens de compilatie typespecifieke code genereert. Deze generieke programmeerstijl is een vorm van statisch polymorfisme die een aanvulling vormt op functie-overloading en operator-overloading.

Wanneer je een afgeleid object verwijdert via een pointer naar de basisklasse, garandeert alleen een virtuele destructor dat de destructor van de afgeleide klasse ook wordt uitgevoerd. Zonder een virtuele destructor is de opruiming onvolledig en ontstaan โ€‹โ€‹er geheugenlekken. Door de destructor van de basisklasse virtueel te declareren, gedraagt โ€‹โ€‹de destructie zich polymorf.

Polymorfisme maakt het mogelijk dat รฉรฉn interface meerdere objecttypen afhandelt, waardoor dubbele code wordt verminderd en het onderhoud wordt vereenvoudigd. Het ondersteunt flexibele, uitbreidbare ontwerpen waarbij nieuwe afgeleide klassen kunnen worden toegevoegd zonder bestaande code te wijzigen, en het maakt krachtige patronen mogelijk die gebaseerd zijn op pointers en referenties naar de basisklasse.

Ja. AI-codeerassistenten zetten een korte opmerking om in overbelaste functies, virtuele basisklassen en afgeleide overschrijvingen. Ze verwerken repetitieve standaardcode snel, maar je moet nog steeds controleren of virtuele trefwoorden, functiesignaturen en destructorveiligheid aanwezig zijn voordat je de gegenereerde code compileert.

Ja. GitHub-copiloot Het suggereert virtuele functiedeclaraties, overschrijvingen van afgeleide klassen en operatoroverloads terwijl je typt. Het versnelt de standaardcode voor overervingshiรซrarchieรซn, maar je moet wel controleren of het sleutelwoord 'virtual', overeenkomende signatures en het juiste late-bindinggedrag correct zijn voordat je gaat compileren.

Vat dit bericht samen met: