C++ Polymorfism med exempel

⚡ Smart sammanfattning

Polymorfism i C++ tillåter en funktion med en enda medlem att anta många former och bete sig olika beroende på vilket objekt som anropar den. Den bygger på arv och förekommer i två varianter, antingen vid kompileringstid eller körning.

  • 🔷 Definition: Polymorfism betyder "många former", vilket låter en medlemsfunktion bete sig olika baserat på det anropande objektet.
  • 🧭 Två typer: C++ tillhandahåller polymorfism vid kompilering (statisk) och polymorfism vid körning (dynamisk).
  • 🧩 Kompilera tid: Funktionsöverbelastning och operatoröverbelastning väljer rätt funktion under kompilering.
  • 🔁 Körtid: Funktionsåsidosättande och virtuella funktioner löser anropet medan programmet körs.
  • ⚙️ Virtuella funktioner: Det virtuella nyckelordet i en basklass möjliggör sen bindning genom en basklass-pekare.
  • 🤖 AI-hjälp: GitHub Copilot och liknande assistenter scaffoldar överbelastade, åsidosatta och virtuella funktioner från en kort kommentar.

C++ polymorfism

Vad är polymorfism i C++?

In C++, polymorfism gör att en medlemsfunktion beter sig olika beroende på vilket objekt som anropar den. Polymorfism är ett grekiskt ord som betyder att ha många former. Det uppstår när du har en hierarki av klasser släkt genom arv.

Anta till exempel att vi har funktionen makeSound(). När en katt anropar denna funktion kommer den att producera ett mjauljud. När en ko åberopar samma funktion, kommer den att ge klippljudet.

Polymorfism i C++ makeSound-exempel

Även om vi har en funktion, beter sig den olika under olika omständigheter. Funktionen har många former; därför har vi uppnått polymorfism.

Med grundidén på plats, C++ organiserar detta beteende i två distinkta kategorier.

Typer av polymorfism

C++ stöder två typer av polymorfism:

  • Kompileringstidspolymorfism, och
  • Runtime polymorfism.

Typer av polymorfism i C++

Den första kategorin löser varje funktionsanrop innan programmet ens körs.

Kompilera tidspolymorfism

Du anropar de överbelastade funktionerna genom att matcha antalet och typen av argument. Informationen finns närvarande under kompileringstiden. Detta betyder C++ kompilatorn väljer rätt funktion vid kompilering.

Kompileringstidspolymorfism uppnås genom funktionsöverbelastning och operatörsöverbelastning.

Funktion Överbelastning

Funktionsöverbelastning uppstår när vi har många funktioner med liknande namn men olika argument. Argumenten kan skilja sig åt vad gäller antal eller typ.

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

Produktion:

C++ funktionsöverbelastning Exempel 1 utgång

Här är en skärmdump av koden:

C++ funktionsöverbelastning Exempel 1 kod

Code Förklaring:

  1. Inkludera iostream-huvudfilen i vår kod. Vi kommer att kunna använda dess funktioner.
  2. Inkludera std-namnområdet i vår kod. Vi kommer att kunna använda dess klasser utan att ringa det.
  3. Skapa en funktion med namnet test som tar en heltalsparameter i. { markerar början av funktionstestet.
  4. Uttalandet som ska utföras om ovanstående funktionsteste anropas/anropas.
  5. Slutet på huvuddelen av ovanstående funktionstest.
  6. Skapa en funktion med namnet test som tar en flytparameter f. { markerar början av funktionstestet.
  7. Uttalandet som ska utföras om ovanstående funktionsteste anropas/anropas.
  8. Slutet på kroppen av ovanstående funktionstest.
  9. Skapa en funktion som heter test som tar en teckenparameter ch. { markerar början av funktionstestet.
  10. Uttalandet som ska utföras om ovanstående funktionsteste anropas/anropas.
  11. Slutet på kroppen av ovanstående funktionstest.
  12. Anropa main()-funktionen. { markerar början av funktionens brödtext.
  13. Anropa funktionstestet och skicka 5 till det som värdet på argumentet. Detta anropar testfunktionen som accepterar ett heltalsargument, det vill säga den första testfunktionen.
  14. Anropa funktionstestet och skicka 5.5 till det som värdet på argumentet. Detta kommer att anropa testfunktionen som accepterar ett flytargument, det vill säga den andra testfunktionen.
  15. Anropa funktionstestet och skicka fem till det som värdet på argumentet. Detta kommer att anropa testfunktionen som accepterar ett teckenargument, det vill säga den tredje testfunktionen.
  16. Programmet måste returnera ett värde om det körs framgångsrikt.
  17. Slutet på huvuddelen av funktionen main().

Vi har tre funktioner med samma namn men olika typer av argument. Vi har uppnått polymorfism.

Operator överbelastning

In OperaFör överbelastning definierar vi en ny betydelse för en C++ Operatören. Det ändrar också hur operatorn fungerar. Till exempel kan vi definiera operatorn + för att sammanfoga två strängar. Vi känner den som additionsoperatorn för att addera numeriska värden. Enligt vår definition, när den placeras mellan heltal, kommer den att addera dem. När den placeras mellan strängar kommer den att sammanfoga dem.

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

Produktion:

C++ operatörsöverbelastning Exempel 2 utgång

Här är en skärmdump av koden:

C++ operatoröverbelastning Exempel 2 kod

C++ operatoröverbelastning Exempel 2 resultat

Code Förklaring:

  1. Inkludera iostream-huvudfilen i vårt program för att kunna använda dess funktioner.
  2. Inkludera std-namnområdet i vårt program för att kunna använda dess klasser utan att anropa det.
  3. Skapa en klass med namnet ComplexNum. { markerar början av klasskroppen.
  4. Använd modifieraren för privat åtkomst för att markera variabler som privata, vilket innebär att de bara kan nås från klassen.
  5. Definiera två heltalsvariabler, reella och över.
  6. Använd modifieraren för offentlig åtkomst för att markera konstruktorn som offentlig, vilket innebär att den kommer att vara tillgänglig även utanför klassen.
  7. Skapa klasskonstruktorn och initiera variablerna.
  8. Initiera värdet på variabeln real.
  9. Initiera värdet på variabeln över.
  10. Slutet på konstruktorkroppen.
  11. Vi måste åsidosätta innebörden av +-operatorn.
  12. Skapa datatypresultatet av typen ComplexNum.
  13. Använd operatorn + med komplexa tal. Denna rad lägger till den reella delen av ett tal till den reella delen av ett annat tal.
  14. Använd operatorn + med komplexa tal. Denna linje kommer att lägga till den imaginära delen av ett tal till den imaginära delen av ett annat tal.
  15. Programmet kommer att returnera värdet på variabelresultatet vid framgångsrik exekvering.
  16. Slutet på definitionen av den nya betydelsen av + operatör, det vill säga överbelastning.
  17. Anropa metoden print().
  18. Skriv ut det nya komplexa numret efter tillägg på konsolen.
  19. Slutet på body of print()-funktionen.
  20. Slutet på brödtexten i klassen ComplexNum.
  21. Anropa main()-funktionen.
  22. Godkänn värdena för både verkliga och komplexa delar som ska läggas till. Den första delen av c1 kommer att läggas till den första delen av c2, det vill säga 10+3. Den andra delen av c1 kommer att läggas till den andra delen av c, det vill säga 2+7.
  23. Utför en operation med operatorn överbelastad + och lagra resultatet i variabel c3.
  24. Skriv ut värdet för variabel c3 på konsolen.
  25. Slutet på huvuddelen av funktionen main().

Där polymorfism vid kompilering avgör tidigt, skjuter den andra kategorin valet upp till exekvering.

Runtime polymorfism

Detta händer när ett objekts metod anropas/anropas under körning snarare än under kompileringstid. Runtime polymorfism uppnås genom funktionsöverstyrning. Funktionen som ska anropas/anropas etableras under körning.

Funktion Åsidosättande

Funktionsöverstyrning inträffar när en funktion av basklassen ges en ny definition i en härledd klass. Vid den tiden kan vi säga att basfunktionen har åsidosatts.

Till exempel:

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

}

Produktion:

C++ funktionsöverskridande utgång

Här är en skärmdump av koden:

C++ funktionsöverskridande kod

Code Förklaring:

  1. Importera iostream-huvudfilen till vårt program för att använda dess funktioner.
  2. Inkludera std-namnområdet i vårt program för att kunna använda dess klasser utan att anropa det.
  3. Skapa en klass som heter Mammal. { markerar början av klasskroppen.
  4. Använd public access-modifieraren för att ställa in funktionen vi ska skapa som allmänt tillgänglig. Den kommer att vara tillgänglig utanför denna klass.
  5. Skapa en offentlig funktion som heter eat. { markerar början av funktionskroppen.
  6. Skriv ut satsen som läggs till cout-funktionen när funktionen eat() anropas.
  7. I slutet av kroppen av funktion eat().
  8. Slutet av kroppen av klassen däggdjur.
  9. Skapa en klass som heter Ko som ärver klassen däggdjur. Ko är den härledda klassen, medan däggdjur är basklassen. { markerar början på denna klass.
  10. Använd public access-modifieraren för att markera funktionen vi håller på att skapa som allmänt tillgänglig. Den kommer att vara tillgänglig utanför denna klass.
  11. Åsidosätt funktionen eat() som definierades i basklassen. { markerar början av funktionskroppen.
  12. Uttalandet som ska skrivas ut på konsolen när denna funktion anropas.
  13. Slutet av kroppen av funktionen eat().
  14. Slutet av kroppen av klassen Ko.
  15. Anropa main()-funktionen. { markerar början på kroppen av denna funktion.
  16. Skapa en instans av Cow-klassen och ge den namnet c.
  17. Anropa eat()-funktionen definierad i Cow-klassen.
  18. Programmet måste returnera ett värde efter framgångsrikt slutförande.
  19. Slutet på main()-funktionen.

C++ Virtuell funktion

En virtuell funktion är ett annat sätt att implementera runtime polymorfism i C++. Det är en speciell funktion som definieras i en basklass och omdefinieras i den härledda klassen. För att deklarera en virtuell funktion bör du använda det virtuella nyckelordet. Nyckelordet ska föregå deklarationen av funktionen i basklassen.

Om en virtuell funktionsklass ärvs, omdefinierar den virtuella funktionen den virtuella funktionen för att passa dess behov. Till exempel:

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

Produktion:

C++ virtuell funktionsutgång

Här är en skärmdump av koden:

C++ virtuell funktionskod

Code Förklaring:

  1. Inkludera iostream-huvudfilen i koden för att använda dess funktioner.
  2. Inkludera std-namnområdet i vår kod för att använda dess klasser utan att anropa det.
  3. Skapa en klass som heter ClassA.
  4. Använd public access modifier för att markera en klassmedlem som allmänt tillgänglig.
  5. Skapa en virtuell funktion som heter show(). Det blir en offentlig funktion.
  6. Texten som ska skrivas ut när show() anropas anropas. Slutet är en C++ nyckelord, vilket betyder slutrad. Den flyttar muspekaren till nästa rad.
  7. Slutet på kroppen av den virtuella funktionen show().
  8. Slutet på kroppen av klassen Klass A.
  9. Skapar en ny klass med namnet ClassB som ärver klassen ClassA. KlassA blir basklassen medan KlassB blir den härledda klassen.
  10. Använd public access modifier för att markera en klassmedlem som allmänt tillgänglig.
  11. Omdefiniera den virtuella funktionen show() som härleds i basklassen.
  12. Texten som ska skrivas ut på konsolen när show()-funktionen definierad i den härledda klassen anropas.
  13. Slutet på brödtexten för funktionen show().
  14. Slutet på kroppen av den härledda klassen, klass B.
  15. Anropa main()-funktionen. Programlogiken bör läggas till i dess kropp.
  16. Skapa en pekarvariabel med namnet a. Den pekar på klassen som heter ClassA.
  17. Skapa en instans av klassen med namnet ClassB. Förekomsten får namnet b.
  18. Tilldela värdena lagras i adressen b i variabeln a.
  19. Anropa show()-funktionen definierad i den härledda klassen. Sen bindning har genomförts.
  20. Slutet på huvuddelen av funktionen main().

Med båda mekanismerna täckta jämförs de sida vid sida i tabellen nedan.

Kompileringstidspolymorfism vs. Run-Time Polymorphism

Här är de stora skillnaderna mellan de två:

Kompileringstid polymorfism Run-time polymorfism
Det kallas också tidig bindning eller statisk polymorfism Det kallas också sen/dynamisk bindning eller dynamisk polymorfism
Metoden anropas/anropas under kompileringstiden Metoden anropas/anropas under körning
Implementeras via funktionsöverbelastning och operatörsöverbelastning Implementeras via metodöverstyrning och virtuella funktioner
Exempel, metodöverbelastning. Många metoder kan ha liknande namn men olika antal eller typer av argument Exempel, överordnad metod. Många metoder kan ha ett liknande namn och samma prototyp.
Snabbare exekvering eftersom metodupptäckten görs under kompileringstiden Långsammare exekvering eftersom metodidentifiering sker under körning.
Less flexibilitet för problemlösning tillhandahålls eftersom allt är känt under kompileringstiden. Mycket flexibilitet tillhandahålls för att lösa komplexa problem eftersom metoder upptäcks under körning.

Vanliga frågor

Överbelastning definierar flera funktioner med samma namn men olika parametrar, som löses vid kompileringstid utan arv. Överstyrning omdefinierar en ärvd basklassfunktion med samma signatur i en härledd klass, som löses vid körning när funktionen deklareras virtuell.

En ren virtuell funktion deklareras med = 0 och har ingen kropp i basklassen. Varje klass som innehåller en blir en abs.tract-klassen, som inte kan instansieras direkt och tvingar härledda klasser att tillhandahålla sin egen implementation.

När en klass deklarerar virtuella funktioner bygger kompilatorn en virtuell tabell (vtable) av funktionspekare. Varje objekt lagrar en dold pekare till sin klass vtable, så en basklass-pekare anropar rätt överskriven funktion vid körning genom sen bindning.

Ja. Mallar låter en funktion eller klass arbeta på många datatyper, där kompilatorn genererar typspecifik kod under kompileringen. Denna generiska programmering är en form av statisk polymorfism som kompletterar funktionsöverbelastning och operatoröverbelastning.

När du tar bort ett härlett objekt via en basklass-pekare, garanterar endast en virtuell destruktor att den härledda destruktorn också körs. Utan den är rensningen ofullständig och orsakar minnesläckor. Att deklarera basdestruktorn som virtuell gör att destruktionen beter sig polymorfiskt.

Polymorfism låter ett gränssnitt hantera många objekttyper, vilket minskar duplicerad kod och förenklar underhåll. Det stöder flexibla, utökningsbara designer där nya härledda klasser kan anslutas utan att ändra befintlig kod, och det möjliggör kraftfulla mönster byggda på basklass-pekare och referenser.

Ja. AI-kodningsassistenter omvandlar en kort kommentar till överbelastade funktioner, virtuella basklasser och härledda overrides. De hanterar repetitiva standardinställningar snabbt, men du bör fortfarande verifiera virtuella nyckelord, funktionssignaturer och destruktorsäkerhet innan du kompilerar den genererade koden.

Ja. GitHub Copilot föreslår deklarationer av virtuella funktioner, åsidosättningar av härledda klasser och operatoröverbelastningar medan du skriver. Den accelererar standardmetoden för arvshierarkier, men du bör bekräfta nyckelordet virtuella, matchande signaturer och korrigera beteendet vid sen bindning innan du bygger.

Sammanfatta detta inlägg med: