C++ Polymorfisme med eksempel

โšก Smart oppsummering

Polymorfisme i C++ lar en enkeltmedlemsfunksjon ta mange former, og oppfรธre seg forskjellig avhengig av objektet som pรฅkaller den. Den bygger pรฅ arv og vises i to varianter, lรธst enten ved kompileringstid eller kjรธretid.

  • ๐Ÿ”ท Definisjon: Polymorfisme betyr ยซmange formerยป, รฅ la รฉn medlemsfunksjon oppfรธre seg annerledes basert pรฅ det kallende objektet.
  • ๐Ÿงญ To typer: C++ gir polymorfisme ved kompilering (statisk) og polymorfisme ved kjรธretid (dynamisk).
  • ๐Ÿงฉ Kompileringstid: Funksjonsoverbelastning og operatoroverbelastning velger riktig funksjon under kompilering.
  • ๐Ÿ” Kjรธretid: Funksjonsoverstyring og virtuelle funksjoner lรธser kallet mens programmet kjรธres.
  • โš™๏ธ Virtuelle funksjoner: Det virtuelle nรธkkelordet i en baseklasse muliggjรธr sen binding gjennom en baseklassepeker.
  • ๐Ÿค– AI-hjelp: GitHub Copilot og lignende assistenter stillaserer overbelastede, overstyrte og virtuelle funksjoner fra en kort kommentar.

C++ polymorfisme

Hva er polymorfisme i C++?

In C++, polymorfisme fรธrer til at en medlemsfunksjon oppfรธrer seg annerledes basert pรฅ objektet som kaller/pรฅkaller den. Polymorfisme er et gresk ord som betyr รฅ ha mange former. Det oppstรฅr nรฅr du har et hierarki av klasser beslektet gjennom arv.

Anta for eksempel at vi har funksjonen makeSound(). Nรฅr en katt kaller denne funksjonen, vil den produsere mjau-lyden. Nรฅr en ku pรฅkaller den samme funksjonen, vil den gi klippelyden.

Polymorfisme i C++ makeSound-eksempel

Selv om vi har รฉn funksjon, oppfรธrer den seg annerledes under forskjellige omstendigheter. Funksjonen har mange former; derfor har vi oppnรฅdd polymorfisme.

Med kjerneideen pรฅ plass, C++ organiserer denne oppfรธrselen i to forskjellige kategorier.

Typer polymorfisme

C++ stรธtter to typer polymorfisme:

  • Kompileringstidspolymorfisme, og
  • Runtime polymorfisme.

Typer av polymorfisme i C++

Den fรธrste kategorien lรธser alle funksjonskall fรธr programmet i det hele tatt kjรธrer.

Kompiler tidspolymorfisme

Du pรฅkaller de overbelastede funksjonene ved รฅ matche antall og type argumenter. Informasjonen er tilstede under kompileringstiden. Dette betyr C++ kompilatoren vil velge riktig funksjon pรฅ kompileringstidspunktet.

Kompileringstidspolymorfisme oppnรฅs gjennom funksjonsoverbelastning og operatรธroverbelastning.

Funksjon Overbelastning

Funksjonsoverbelastning oppstรฅr nรฅr vi har mange funksjoner med like navn, men forskjellige argumenter. Argumentene kan variere nรฅr det gjelder antall eller type.

Eksempel 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;
}

Utgang:

C++ funksjonsoverbelastning Eksempel 1 utgang

Her er et skjermbilde av koden:

C++ funksjonsoverbelastning Eksempel 1 kode

Code Forklaring:

  1. Inkluder iostream-headerfilen i koden vรฅr. Vi vil kunne bruke funksjonene.
  2. Inkluder std-navneomrรฅdet i koden vรฅr. Vi vil kunne bruke klassene uten รฅ ringe det.
  3. Opprett en funksjon kalt test som tar en heltallsparameter i. { markerer begynnelsen pรฅ funksjonstesten.
  4. Uttalelse som skal utfรธres hvis funksjonstesten ovenfor pรฅkalles/kalles.
  5. Slutten av hoveddelen av funksjonstesten ovenfor.
  6. Opprett en funksjon kalt test som tar en flytparameter f. { markerer begynnelsen pรฅ funksjonstesten.
  7. Uttalelse som skal utfรธres hvis funksjonstesten ovenfor pรฅkalles/kalles.
  8. Slutten av hoveddelen av funksjonstesten ovenfor.
  9. Lag en funksjon kalt test som tar en tegnparameter ch. { markerer begynnelsen pรฅ funksjonstesten.
  10. Uttalelse som skal utfรธres hvis funksjonstesten ovenfor pรฅkalles/kalles.
  11. Slutten av hoveddelen av funksjonstesten ovenfor.
  12. Kalle hoved()-funksjonen. { markerer begynnelsen pรฅ kroppen til funksjonen.
  13. Ring funksjonstesten og send 5 til den som verdien av argumentet. Dette pรฅkaller testfunksjonen som godtar et heltallsargument, det vil si den fรธrste testfunksjonen.
  14. Kall funksjonstesten og send 5.5 til den som verdien av argumentet. Dette vil pรฅkalle testfunksjonen som godtar et flytargument, det vil si den andre testfunksjonen.
  15. Ring funksjonstesten og send fem til den som verdien av argumentet. Dette vil pรฅkalle testfunksjonen som godtar et tegnargument, det vil si den tredje testfunksjonen.
  16. Programmet mรฅ returnere en verdi hvis det kjรธrer vellykket.
  17. Slutten av hoveddelen av hoved()-funksjonen.

Vi har tre funksjoner med samme navn, men forskjellige typer argumenter. Vi har oppnรฅdd polymorfisme.

Operator overbelastning

In Operafor overbelastning, definerer vi en ny betydning for en C++ operatรธr. Det endrer ogsรฅ hvordan operatoren fungerer. For eksempel kan vi definere + -operatoren for รฅ sammenkoble to strenger. Vi kjenner den som addisjonsoperatoren for รฅ legge sammen numeriske verdier. Etter vรฅr definisjon, nรฅr den plasseres mellom heltall, vil den legge dem sammen. Nรฅr den plasseres mellom strenger, vil den sammenkoble dem.

Eksempel 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();
}

Utgang:

C++ operatรธroverbelastning Eksempel 2 utgang

Her er et skjermbilde av koden:

C++ Eksempel 2 kode for operatoroverbelastning

C++ operatรธroverbelastning Eksempel 2 resultat

Code Forklaring:

  1. Inkluder iostream-headerfilen i programmet vรฅrt for รฅ bruke funksjonene.
  2. Inkluder std-navneomrรฅdet i programmet vรฅrt for รฅ bruke klassene uten รฅ kalle det.
  3. Lag en klasse som heter ComplexNum. { markerer begynnelsen pรฅ klasseteksten.
  4. Bruk modifikatoren for privat tilgang til รฅ merke variabler som private, noe som betyr at de kun kan nรฅs fra klassen.
  5. Definer to heltallsvariabler, reelle og over.
  6. Bruk modifikatoren for offentlig tilgang til รฅ markere konstruktรธren som offentlig, som betyr at den vil vรฆre tilgjengelig selv utenfra klassen.
  7. Lag klassekonstruktรธren og initialiser variablene.
  8. Initialiser verdien av variabelen reell.
  9. Initialiser verdien av variabelen over.
  10. Slutten av konstruktรธrkroppen.
  11. Vi mรฅ overstyre betydningen av +-operatoren.
  12. Opprett datatyperesultatet av typen ComplexNum.
  13. Bruk operatoren + med komplekse tall. Denne linjen vil legge til den reelle delen av et tall til den reelle delen av et annet tall.
  14. Bruk operatoren + med komplekse tall. Denne linjen vil legge til den imaginรฆre delen av et tall til den imaginรฆre delen av et annet tall.
  15. Programmet vil returnere verdien av variabelresultatet ved vellykket utfรธrelse.
  16. Slutten av definisjonen av den nye betydningen av + operatรธr, det vil si overbelastning.
  17. Kall opp print()-metoden.
  18. Skriv ut det nye komplekse tallet etter addisjon pรฅ konsollen.
  19. Slutten av hovedteksten () funksjon.
  20. Slutten av brรธdteksten til ComplexNum-klassen.
  21. Kalle hoved()-funksjonen.
  22. Send verdiene til bรฅde reelle og komplekse deler som skal legges til. Den fรธrste delen av c1 vil bli lagt til den fรธrste delen av c2, det vil si 10+3. Den andre delen av c1 vil bli lagt til den andre delen av c, det vil si 2+7.
  23. Utfรธr en operasjon ved รฅ bruke overbelastet +-operatoren og lagre resultatet i variabel c3.
  24. Skriv ut verdien av variabel c3 pรฅ konsollen.
  25. Slutten av hoveddelen av funksjonen main().

Der polymorfisme ved kompilering avgjรธr tidlig, utsetter den andre kategorien valget til utfรธrelse.

Runtime polymorfisme

Dette skjer nรฅr et objekts metode pรฅkalles/kalles under kjรธring i stedet for under kompileringstiden. Runtime polymorfisme oppnรฅs gjennom funksjonsoverstyring. Funksjonen som skal kalles/pรฅkalles etableres under kjรธring.

Funksjonsoverstyring

Funksjonsoverstyring skjer nรฅr en funksjon av basisklassen fรฅr en ny definisjon i en avledet klasse. Pรฅ det tidspunktet kan vi si at basisfunksjonen har blitt overstyrt.

For eksempel:

#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;

}

Utgang:

C++ funksjonsoverstyrende utgang

Her er et skjermbilde av koden:

C++ funksjonsoverstyrende kode

Code Forklaring:

  1. Importer iostream-headerfilen til programmet vรฅrt for รฅ bruke funksjonene.
  2. Inkluder std-navneomrรฅdet i programmet vรฅrt for รฅ bruke klassene uten รฅ kalle det.
  3. Lag en klasse som heter Mammal. { markerer begynnelsen av klasseteksten.
  4. Bruk modifikatoren for offentlig tilgang til รฅ angi funksjonen vi skal lage som offentlig tilgjengelig. Den vil vรฆre tilgjengelig utenfor denne klassen.
  5. Lag en offentlig funksjon som heter eat. { markerer begynnelsen av funksjonsteksten.
  6. Skriv ut setningen som er lagt til cout-funksjonen nรฅr funksjonen eat() pรฅkalles.
  7. Slutten av funksjonskroppen spise().
  8. Slutten av kroppen til klassen Pattedyr.
  9. Lag en klasse som heter Cow som arver pattedyrklassen. Ku er den avledede klassen, mens pattedyr er basisklassen. { markerer begynnelsen pรฅ denne klassen.
  10. Bruk modifikatoren for offentlig tilgang til รฅ merke funksjonen vi er i ferd med รฅ lage som offentlig tilgjengelig. Den vil vรฆre tilgjengelig utenfor denne klassen.
  11. Overstyr funksjonen eat() som ble definert i basisklassen. { markerer begynnelsen av funksjonsteksten.
  12. Uttalelsen som skal skrives ut pรฅ konsollen nรฅr denne funksjonen aktiveres.
  13. Slutten av kroppen av funksjonen eat().
  14. Slutten av kroppen til klassen Ku.
  15. Kalle hoved()-funksjonen. { markerer begynnelsen pรฅ hoveddelen av denne funksjonen.
  16. Lag en forekomst av Cow-klassen og gi den navnet c.
  17. Kall eat()-funksjonen definert i Cow-klassen.
  18. Programmet mรฅ returnere en verdi ved vellykket gjennomfรธring.
  19. Slutten av hoved()-funksjonen.

C++ Virtuell funksjon

En virtuell funksjon er en annen mรฅte รฅ implementere run-time polymorfisme i C++. Det er en spesiell funksjon definert i en basisklasse og redefinert i den avledede klassen. For รฅ erklรฆre en virtuell funksjon, bรธr du bruke det virtuelle nรธkkelordet. Nรธkkelordet skal gรฅ foran deklarasjonen av funksjonen i basisklassen.

Hvis en virtuell funksjonsklasse er arvet, redefinerer den virtuelle klassen den virtuelle funksjonen for รฅ passe dens behov. For eksempel:

#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();      
	}

Utgang:

C++ virtuell funksjonsutgang

Her er et skjermbilde av koden:

C++ virtuell funksjonskode

Code Forklaring:

  1. Inkluder iostream-headerfilen i koden for รฅ bruke funksjonene.
  2. Inkluder std-navneomrรฅdet i koden vรฅr for รฅ bruke klassene uten รฅ kalle det.
  3. Lag en klasse som heter ClassA.
  4. Bruk modifikatoren for offentlig tilgang til รฅ merke et klassemedlem som offentlig tilgjengelig.
  5. Lag en virtuell funksjon kalt show(). Det blir en offentlig funksjon.
  6. Teksten som skal skrives ut nรฅr show() pรฅkalles, pรฅkalles. Endl er en C++ nรธkkelord, som betyr sluttlinje. Den flytter musepekeren til neste linje.
  7. Slutten av kroppen til den virtuelle funksjonen show().
  8. Slutten av brรธdteksten til klassen KlasseA.
  9. Opprette en ny klasse kalt ClassB som arver klassen ClassA. KlasseA blir basisklassen mens KlasseB blir den avledede klassen.
  10. Bruk modifikatoren for offentlig tilgang til รฅ merke et klassemedlem som offentlig tilgjengelig.
  11. Redefiner den virtuelle funksjonen show() utledet i basisklassen.
  12. Teksten som skal skrives ut pรฅ konsollen nรฅr show()-funksjonen definert i den avledede klassen pรฅkalles.
  13. Slutten av brรธdteksten til show()-funksjonen.
  14. Slutten av kroppen til den avledede klassen, KlasseB.
  15. Kalle hoved()-funksjonen. Programlogikken bรธr legges til i hoveddelen.
  16. Lag en pekervariabel kalt a. Den peker pรฅ klassen som heter ClassA.
  17. Opprett en forekomst av klassen kalt ClassB. Forekomsten fรฅr navnet b.
  18. Tilordne verdiene lagres i adressen b i variabelen a.
  19. Pรฅkall show()-funksjonen definert i den avledede klassen. Sen binding er iverksatt.
  20. Slutten av hoveddelen av hoved()-funksjonen.

Tabellen nedenfor viser begge mekanismene side om side, og de er dekket.

Kompileringstidspolymorfisme vs. Run-Time Polymorphism

Her er de viktigste forskjellene mellom de to:

Kompileringstidspolymorfisme Run-time polymorfisme
Det kalles ogsรฅ tidlig binding eller statisk polymorfisme Det kalles ogsรฅ sen/dynamisk binding eller dynamisk polymorfisme
Metoden kalles/pรฅkalles under kompileringstiden Metoden kalles/pรฅkalles under kjรธretid
Implementert via funksjonsoverbelastning og operatรธroverbelastning Implementert via metodeoverstyring og virtuelle funksjoner
Eksempel, metodeoverbelastning. Mange metoder kan ha lignende navn, men forskjellige antall eller typer argumenter Eksempel, metodeoverstyring. Mange metoder kan ha et lignende navn og samme prototype.
Raskere utfรธrelse siden metodeoppdagelsen gjรธres under kompileringstiden Tregere utfรธrelse siden metodeoppdagelse gjรธres under kjรธretid.
Less fleksibilitet for problemlรธsning er gitt siden alt er kjent under kompileringstiden. Mye fleksibilitet er gitt for รฅ lรธse komplekse problemer siden metoder oppdages under kjรธring.

Spรธrsmรฅl og svar

Overbelastning definerer flere funksjoner med samme navn men forskjellige parametere, lรธst ved kompileringstid uten arv. Overstyring omdefinerer en arvet baseklassefunksjon med samme signatur i en avledet klasse, lรธst ved kjรธretid nรฅr funksjonen erklรฆres virtuell.

En ren virtuell funksjon deklareres med = 0 og har ingen kropp i basisklassen. Enhver klasse som inneholder en blir en abs.tract-klassen, som ikke kan instansieres direkte og tvinger avledede klasser til รฅ levere sin egen implementering.

Nรฅr en klasse deklarerer virtuelle funksjoner, bygger kompilatoren en virtuell tabell (vtable) med funksjonspekere. Hvert objekt lagrer en skjult peker til sin klasse vtable, slik at en peker i basisklassen kaller den riktige overstyrte funksjonen ved kjรธretid gjennom sen binding.

Ja. Maler lar รฉn funksjon eller klasse operere pรฅ mange datatyper, der kompilatoren genererer typespesifikk kode under kompileringen. Denne generiske programmeringen er en form for statisk polymorfisme som utfyller funksjonsoverbelastning og operatoroverbelastning.

Nรฅr du sletter et avledet objekt gjennom en peker av baseklassen, er det bare en virtuell destruktor som garanterer at den avledede destruktoren ogsรฅ kjรธrer. Uten den er oppryddingen ufullstendig og forรฅrsaker minnelekkasjer. ร… deklarere basedestruktoren som virtuell fรฅr destruksjonen til รฅ oppfรธre seg polymorfisk.

Polymorfisme lar ett grensesnitt hรฅndtere mange objekttyper, noe som reduserer duplisert kode og forenkler vedlikehold. Det stรธtter fleksible, utvidbare design der nye avledede klasser kobles til uten รฅ endre eksisterende kode, og det muliggjรธr kraftige mรธnstre bygget pรฅ pekere og referanser i basisklassen.

Ja. AI-kodingsassistenter gjรธr en kort kommentar om til overbelastede funksjoner, virtuelle basisklasser og avledede overstyringer. De hรฅndterer repeterende standardtekst raskt, men du bรธr fortsatt bekrefte virtuelle nรธkkelord, funksjonssignaturer og destruktorsikkerhet fรธr du kompilerer den genererte koden.

Ja. GitHub Copilot foreslรฅr deklarasjoner av virtuelle funksjoner, overstyringer av avledede klasser og operatoroverbelastning mens du skriver. Den akselererer standardmetoden for arvshierarkier, men du bรธr bekrefte det virtuelle nรธkkelordet, samsvarende signaturer og korrigere sen bindingsvirkemรฅte fรธr du bygger.

Oppsummer dette innlegget med: