C++ Polimorfism cu Exemplu

⚡ Rezumat inteligent

Polimorfismul în C++ permite unei singure funcții membru să ia mai multe forme, comportându-se diferit în funcție de obiectul care o invocă. Se bazează pe moștenire și apare în două variante, rezolvate fie la momentul compilării, fie la momentul execuției.

  • 🔷 Definiție: Polimorfismul înseamnă „mai multe forme”, permițând unei funcții membre să se comporte diferit în funcție de obiectul apelant.
  • 🧭 Doua tipuri: C++ oferă polimorfism în timpul compilării (static) și polimorfism în timpul execuției (dinamic).
  • 🧩 Timp de compilare: Supraîncărcarea funcțiilor și supraîncărcarea operatorilor selectează funcția corectă în timpul compilării.
  • 🔁 Timp de rulare: Suprascrierea funcțiilor și funcțiile virtuale rezolvă apelul în timp ce programul se execută.
  • ⚙️ Funcții virtuale: Cuvântul cheie virtual dintr-o clasă de bază permite legarea târzie printr-un pointer al clasei de bază.
  • 🤖 Asistență AI: GitHub Copilot și asistenți similari creează funcții supraîncărcate, suprascrise și virtuale dintr-un comentariu scurt.

C++ polimorfismul

În ce este polimorfismul C++?

In C++, polimorfismul face ca o funcție membru să se comporte diferit în funcție de obiectul care o apelează/invocă. Polimorfismul este un cuvânt grecesc care înseamnă a avea mai multe forme. Apare atunci când aveți o ierarhie de clase înrudite prin moștenire.

De exemplu, să presupunem că avem funcția makeSound(). Când o pisică apelează această funcție, va produce sunetul miau. Atunci când o vacă invocă aceeași funcție, va furniza sunetul de cosuit.

Polimorfismul în C++ exemplu makeSound

Deși avem o singură funcție, aceasta se comportă diferit în diferite circumstanțe. Funcția are multe forme; prin urmare, am atins polimorfismul.

Cu ideea centrală stabilită, C++ organizează acest comportament în două categorii distincte.

Tipuri de polimorfism

C++ suportă două tipuri de polimorfism:

  • polimorfism în timp de compilare și
  • Polimorfismul runtime.

Tipuri de polimorfism în C++

Prima categorie rezolvă fiecare apel de funcție înainte ca programul să ruleze vreodată.

Polimorfismul timpului de compilare

Invocați funcțiile supraîncărcate prin potrivirea numărului și tipului de argumente. Informațiile sunt prezente în timpul compilării. Aceasta înseamnă C++ compilatorul va selecta funcția potrivită în timpul compilării.

Polimorfismul în timp de compilare se realizează prin supraîncărcarea funcțiilor și supraîncărcarea operatorului.

Supraîncărcarea funcției

Supraîncărcarea funcțiilor apare atunci când avem multe funcții cu nume similare, dar cu argumente diferite. Argumentele pot diferi în ceea ce privește numărul sau tipul.

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

ieșire:

C++ supraîncărcarea funcției Exemplul 1 de ieșire

Iată o captură de ecran a codului:

C++ supraîncărcarea funcțiilor Exemplul 1 de cod

Code Explicaţie:

  1. Includeți fișierul antet iostream în codul nostru. Vom putea folosi funcțiile sale.
  2. Includeți spațiul de nume std în codul nostru. Vom putea folosi clasele sale fără a-l apela.
  3. Creați o funcție numită test care ia un parametru întreg i. { marchează începutul corpului testului funcțional.
  4. Declarație care trebuie executată dacă testul funcției de mai sus este invocat/apelat.
  5. Sfârșitul corpului testului funcțional de mai sus.
  6. Creați o funcție numită test care ia un parametru float f. { marchează începutul corpului testului funcțional.
  7. Declarație care trebuie executată dacă testul funcției de mai sus este invocat/apelat.
  8. Sfârșitul corpului testului funcțional de mai sus.
  9. Creați o funcție numită test care ia un parametru de caracter ch. { marchează începutul corpului testului funcțional.
  10. Declarație care trebuie executată dacă testul funcției de mai sus este invocat/apelat.
  11. Sfârșitul corpului testului funcțional de mai sus.
  12. Apelați funcția main(). { marchează începutul corpului funcției.
  13. Apelați testul funcției și treceți-i 5 ca valoare a argumentului. Aceasta invocă funcția de testare care acceptă un argument întreg, adică prima funcție de testare.
  14. Apelați testul funcției și treceți-i 5.5 ca valoare a argumentului. Aceasta va invoca funcția de testare care acceptă un argument flotant, adică a doua funcție de testare.
  15. Apelați testul funcției și treceți-i cinci ca valoare a argumentului. Aceasta va invoca funcția de testare care acceptă un argument caracter, adică a treia funcție de testare.
  16. Programul trebuie să returneze o valoare dacă rulează cu succes.
  17. Sfârșitul corpului funcției main().

Avem trei funcții cu același nume, dar diferite tipuri de argumente. Am atins polimorfismul.

Operator Supraîncărcare

In Operator Supraîncărcare, definim un nou sens pentru a C++ operatorDe asemenea, modifică modul de funcționare al operatorului. De exemplu, putem defini operatorul + pentru a concatena două șiruri de caractere. Îl cunoaștem ca operator de adunare pentru adunarea valorilor numerice. Conform definiției noastre, atunci când este plasat între numere întregi, le va aduna. Când este plasat între șiruri de caractere, le va concatena.

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

ieșire:

C++ supraîncărcarea operatorului Exemplul 2 ieșire

Iată o captură de ecran a codului:

C++ supraîncărcarea operatorilor Exemplul 2 de cod

C++ supraîncărcarea operatorilor Exemplul 2 rezultat

Code Explicaţie:

  1. Includeți fișierul antet iostream în programul nostru pentru a utiliza funcțiile acestuia.
  2. Includeți spațiul de nume std în programul nostru pentru a utiliza clasele sale fără a-l apela.
  3. Creați o clasă numită ComplexNum. { marchează începutul corpului clasei.
  4. Utilizați modificatorul de acces privat pentru a marca variabilele ca private, ceea ce înseamnă că pot fi accesate numai din cadrul clasei.
  5. Definiți două variabile întregi, reale și peste.
  6. Folosește modificatorul de acces public pentru a marca constructorul ca fiind public, ceea ce înseamnă că va fi accesibil chiar și din afara clasei.
  7. Creați constructorul clasei și inițializați variabilele.
  8. Inițializați valoarea variabilei real.
  9. Inițializați valoarea variabilei peste.
  10. Sfârșitul corpului constructorului.
  11. Trebuie să suprascriem semnificația operatorului +.
  12. Creați rezultatul tipului de date de tip ComplexNum.
  13. Utilizați operatorul + cu numere complexe. Această linie va adăuga partea reală a unui număr la partea reală a altui număr.
  14. Utilizați operatorul + cu numere complexe. Această linie va adăuga partea imaginară a unui număr la partea imaginară a altui număr.
  15. Programul va returna valoarea rezultatului variabilei la executarea cu succes.
  16. Sfârșitul definiției noului sens al operatorului +, adică supraîncărcarea.
  17. Apelați metoda print().
  18. Tipăriți noul număr complex după adăugare pe consolă.
  19. Sfârșitul corpului funcției print().
  20. Sfârșitul corpului clasei ComplexNum.
  21. Apelați funcția main().
  22. Transmiteți valorile atât ale părților reale, cât și ale părților complexe care urmează să fie adăugate. Prima parte a lui c1 va fi adăugată la prima parte a lui c2, adică 10+3. A doua parte a lui c1 va fi adăugată la a doua parte a lui c, adică 2+7.
  23. Efectuați o operație folosind operatorul supraîncărcat + și stocând rezultatul în variabila c3.
  24. Tipăriți valoarea variabilei c3 pe consolă.
  25. Sfârșitul corpului funcției main().

În timp ce polimorfismul în timpul compilării decide devreme, a doua categorie amână alegerea până la execuție.

Polimorfismul în timpul rulării

Acest lucru se întâmplă atunci când metoda unui obiect este invocată/apelată în timpul rulării, mai degrabă decât în ​​timpul compilării. Polimorfismul runtime se realizează prin suprascrierea funcției. Funcția care trebuie apelată/invocată este stabilită în timpul rulării.

Suprascrierea funcției

Suprascrierea funcției are loc atunci când unei funcții a clasei de bază i se dă o nouă definiție într-o clasă derivată. În acel moment, putem spune că funcția de bază a fost înlocuită.

De exemplu:

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

}

ieșire:

C++ ieșire care suprascrie funcția

Iată o captură de ecran a codului:

C++ cod de suprascriere a funcțiilor

Code Explicaţie:

  1. Importați fișierul antet iostream în programul nostru pentru a utiliza funcțiile acestuia.
  2. Includeți spațiul de nume std în programul nostru pentru a utiliza clasele sale fără a-l apela.
  3. Creați o clasă numită Mamifer. { marchează începutul corpului clasei.
  4. Utilizați modificatorul de acces public pentru a seta funcția pe care urmează să o creăm ca fiind accesibilă publicului. Acesta va fi accesibil din afara acestei clase.
  5. Creați o funcție publică numită eat. { marchează începutul corpului funcției.
  6. Imprimați instrucțiunea adăugată la funcția cout când este invocată funcția eat().
  7. Sfârșitul corpului funcției eat().
  8. Sfârșitul corpului clasei Mamifer.
  9. Creați o clasă numită Cow care moștenește clasa Mammal. Vaca este clasa derivată, în timp ce Mamiferul este clasa de bază. { marchează începutul acestei clase.
  10. Utilizați modificatorul de acces public pentru a marca funcția pe care urmează să o creăm ca fiind accesibilă publicului. Acesta va fi accesibil din afara acestei clase.
  11. Ignorați funcția eat() care a fost definită în clasa de bază. { marchează începutul corpului funcției.
  12. Declarația de tipărit pe consolă atunci când această funcție este invocată.
  13. Sfârșitul corpului funcției eat().
  14. Sfârșitul corpului clasei Vaca.
  15. Apelați funcția main(). { marchează începutul corpului acestei funcții.
  16. Creați o instanță a clasei Cow și dându-i numele c.
  17. Apelați funcția eat() definită în clasa Cow.
  18. Programul trebuie să returneze o valoare la finalizarea cu succes.
  19. Sfârșitul funcției main().

C++ Funcția virtuală

O funcție virtuală este o altă modalitate de implementare a polimorfismului în timp de rulare în C++. Este o funcție specială definită într-o clasă de bază și redefinită în clasa derivată. Pentru a declara o funcție virtuală, ar trebui să utilizați cuvântul cheie virtual. Cuvântul cheie ar trebui să preceadă declararea funcției în clasa de bază.

Dacă o clasă de funcție virtuală este moștenită, clasa virtuală redefinește funcția virtuală pentru a se potrivi nevoilor sale. De exemplu:

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

ieșire:

C++ ieșire funcție virtuală

Iată o captură de ecran a codului:

C++ codul funcției virtuale

Code Explicaţie:

  1. Includeți fișierul antet iostream în cod pentru a utiliza funcțiile acestuia.
  2. Includeți spațiul de nume std în codul nostru pentru a-i folosi clasele fără a-l apela.
  3. Creați o clasă numită ClassA.
  4. Utilizați modificatorul de acces public pentru a marca un membru al clasei ca fiind accesibil public.
  5. Creați o funcție virtuală numită show(). Va fi o funcție publică.
  6. Textul de imprimat atunci când show() invocat este invocat. Sfârșitul este a C++ cuvânt cheie, care înseamnă linia finală. Mută ​​cursorul mouse-ului pe linia următoare.
  7. Sfârșitul corpului funcției virtuale show().
  8. Sfârșitul corpului clasei ClassA.
  9. Crearea unei noi clase numite ClassB care moștenește clasa ClassA. ClassA devine clasa de bază, în timp ce ClassB devine clasa derivată.
  10. Utilizați modificatorul de acces public pentru a marca un membru al clasei ca fiind accesibil public.
  11. Redefiniți funcția virtuală show() derivată în clasa de bază.
  12. Textul de tipărit pe consolă când este invocată funcția show() definită în clasa derivată.
  13. Sfârșitul corpului funcției show().
  14. Sfârșitul corpului clasei derivate, ClassB.
  15. Apelați funcția main(). Logica programului ar trebui adăugată în corpul său.
  16. Creați o variabilă pointer numită a. Indică clasa numită ClassA.
  17. Creați o instanță a clasei numită ClassB. Instanța primește numele b.
  18. Atribuiți stocurile de valori în adresa b în variabila a.
  19. Invocați funcția show() definită în clasa derivată. Legarea tardivă a fost implementată.
  20. Sfârșitul corpului funcției main().

Având în vedere că ambele mecanisme sunt acoperite, tabelul de mai jos le prezintă în contrast.

Polimorfismul în timp de compilare vs. Polimorfismul în timp de rulare

Iată diferențele majore dintre cele două:

Polimorfism în timp de compilare Polimorfismul timpului de rulare
Se mai numește și polimorfism de legare timpurie sau polimorfism static Se mai numește și legare târzie/dinamică sau polimorfism dinamic
Metoda este apelată/invocată în timpul compilării Metoda este apelată/invocată în timpul rulării
Implementat prin supraîncărcarea funcțiilor și supraîncărcarea operatorului Implementat prin suprascrierea metodei și funcții virtuale
De exemplu, supraîncărcarea metodei. Multe metode pot avea nume similare, dar număr sau tipuri diferite de argumente Exemplu, înlocuirea metodei. Multe metode pot avea un nume similar și același prototip.
Execuție mai rapidă deoarece descoperirea metodei se face în timpul compilării Execuție mai lentă deoarece descoperirea metodelor se face în timpul execuției.
Less este oferită flexibilitate pentru rezolvarea problemelor, deoarece totul este cunoscut în timpul compilării. Este oferită multă flexibilitate pentru rezolvarea problemelor complexe, deoarece metodele sunt descoperite în timpul rulării.

Întrebări frecvente

Supraîncărcarea definește mai multe funcții cu același nume, dar cu parametri diferiți, rezolvate la momentul compilării fără moștenire. Suprascrierea redefinește o funcție a clasei de bază moștenită cu aceeași semnătură într-o clasă derivată, rezolvată la momentul execuției, când funcția este declarată virtuală.

O funcție virtuală pură este declarată cu = 0 și nu are corp în clasa de bază. Orice clasă care conține unul devine un corp abs.tracClasa t, care nu poate fi instanțiată direct și obligă clasele derivate să furnizeze propria implementare.

Când o clasă declară funcții virtuale, compilatorul construiește un tabel virtual (vtable) de pointeri de funcții. Fiecare obiect stochează un pointer ascuns către clasa sa vtable, astfel încât un pointer al clasei de bază apelează funcția corectă suprascrisă la momentul execuției prin intermediul legării târzii.

Da. Șabloanele permit unei funcții sau clase să opereze pe mai multe tipuri de date, compilatorul generând cod specific tipului în timpul compilării. Această programare generică este o formă de polimorfism static care completează supraîncărcarea funcțiilor și supraîncărcarea operatorilor.

Când ștergeți un obiect derivat printr-un pointer al clasei de bază, doar un destructor virtual garantează că și destructorul derivat rulează. Fără acesta, curățarea este incompletă și provoacă pierderi de memorie. Declararea destructorului de bază ca virtual face ca distrugerea să se comporte polimorfic.

Polimorfismul permite unei singure interfețe să gestioneze mai multe tipuri de obiecte, reducând codul duplicat și simplificând mentenanța. Acesta suportă designuri flexibile și extensibile în care noile clase derivate se integrează fără a modifica codul existent și permite modele puternice construite pe pointeri și referințe ale clasei de bază.

Da. Asistenții de codare bazați pe inteligență artificială transformă un comentariu scurt în funcții supraîncărcate, clase de bază virtuale și suprascrieri derivate. Aceștia gestionează rapid codul repetitiv, deși ar trebui să verificați în continuare cuvintele cheie virtuale, semnăturile funcțiilor și siguranța destructorului înainte de a compila codul generat.

Da. Copilotul GitHub sugerează declarații de funcții virtuale, suprascrieri ale claselor derivate și supraîncărcări ale operatorilor pe măsură ce tastați. Accelerează procesul standard pentru ierarhiile de moștenire, deși ar trebui să confirmați cuvântul cheie virtual, semnăturile potrivite și să corectați comportamentul de legare târzie înainte de construire.

Rezumați această postare cu: