C++ Polymorfismi esimerkin kanssa

⚡ Älykäs yhteenveto

Polymorfismi sisään C++ sallii yksittäisen jäsenfunktion ottaa monia muotoja, jotka käyttäytyvät eri tavoin riippuen sitä kutsuvasta objektista. Se perustuu periytymiseen ja esiintyy kahtena versiona, jotka ratkaistaan ​​joko käännösaikana tai ajonaikana.

  • 🔷 Määritelmä: Polymorfismi tarkoittaa "monta muotoa", jossa yksi jäsenfunktio voi käyttäytyä eri tavalla kutsuvasta objektista riippuen.
  • 🧭 Kaksi tyyppiä: C++ tarjoaa käännösaikaisen (staattisen) polymorfismin ja ajonaikaisen (dynaamisen) polymorfismin.
  • 🧩 Käännösaika: Funktion ylikuormitus ja operaattorin ylikuormitus valitsevat oikean funktion käännöksen aikana.
  • 🔁 Suoritusaika: Funktion ohittaminen ja virtuaalifunktiot ratkaisevat kutsun ohjelman suorituksen aikana.
  • ⚙️ Virtuaalifunktiot: Perusluokan virtual-avainsana mahdollistaa myöhäisen sidonnan perusluokan osoittimen kautta.
  • 🤖 AI-apu: GitHub Copilot ja vastaavat avustajat luovat lyhyestä kommentista telineellisiä ylikuormitettuja, ohitettuja ja virtuaalisia funktioita.

C++ polymorfismi

Mitä polymorfismi sisältää C++?

In C++, polymorfismi saa jäsenfunktion käyttäytymään eri tavalla sen kutsuvan/aktivoivan objektin mukaan. Polymorfismi on kreikan kielen sana, joka tarkoittaa "monimuotoista". Se tapahtuu, kun sinulla on hierarkia luokat sukua perinnön kautta.

Oletetaan esimerkiksi, että meillä on funktio makeSound(). Kun kissa kutsuu tätä toimintoa, se tuottaa miau-äänen. Kun lehmä käyttää samaa toimintoa, se antaa moow-äänen.

Polymorfismi sisään C++ makeSound-esimerkki

Vaikka meillä on yksi toiminto, se käyttäytyy eri tavalla eri olosuhteissa. Funktiolla on monia muotoja; näin ollen olemme saavuttaneet polymorfismin.

Kun ydinajatus on kohdallaan, C++ järjestää tämän käyttäytymisen kahteen erilliseen luokkaan.

Polymorfismin tyypit

C++ tukee kahden tyyppistä polymorfismia:

  • Käännösajan polymorfismi ja
  • Ajonaikainen polymorfismi.

Polymorfismin tyypit C++

Ensimmäinen luokka ratkaisee jokaisen funktiokutsun ennen ohjelman suorittamista.

Kokoa aikapolymorfismi

Kutsut ylikuormitetut funktiot sovittamalla argumenttien lukumäärän ja tyypin. Tiedot ovat esillä käännösaikana. Tämä tarkoittaa C++ kääntäjä valitsee oikean funktion käännöshetkellä.

Käännösajan polymorfismi saavutetaan toimintojen ylikuormituksella ja operaattorin ylikuormituksella.

Toimintojen ylikuormitus

Funktioiden ylikuormitus tapahtuu, kun meillä on monia funktioita, joilla on samanlaiset nimet, mutta eri argumentit. Argumentit voivat vaihdella lukumäärän tai tyypin suhteen.

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

lähtö:

C++ funktion ylikuormitus Esimerkki 1 tuloste

Tässä on kuvakaappaus koodista:

C++ funktion ylikuormitus esimerkki 1 koodi

Code Selitys:

  1. Sisällytä iostream-otsikkotiedosto koodiimme. Pystymme käyttämään sen toimintoja.
  2. Sisällytä std-nimiavaruus koodiimme. Voimme käyttää sen luokkia soittamatta siihen.
  3. Luo funktio nimeltä testi, joka ottaa kokonaislukuparametrin i. { merkitsee toimintatestin alkua.
  4. Suoritettava lauseke, jos yllä oleva toimintotesti vedetään/kutsutaan.
  5. Yllä olevan toimintatestin rungon loppu.
  6. Luo funktio nimeltä testi, joka ottaa float-parametrin f. { merkitsee toimintatestin alkua.
  7. Suoritettava lauseke, jos yllä oleva toimintotesti vedetään/kutsutaan.
  8. Yllä olevan toimintatestin rungon loppu.
  9. Luo funktio nimeltä testi, joka ottaa merkkiparametrin ch. { merkitsee toimintatestin alkua.
  10. Suoritettava lauseke, jos yllä oleva toimintotesti vedetään/kutsutaan.
  11. Yllä olevan toimintatestin rungon loppu.
  12. Kutsu main()-funktiota. { merkitsee funktion rungon alkua.
  13. Kutsu funktio testi ja syötä sille 5 argumentin arvoksi. Tämä käynnistää testifunktion, joka hyväksyy kokonaislukuargumentin, eli ensimmäisen testifunktion.
  14. Kutsu funktiotesti ja syötä sille 5.5 argumentin arvoksi. Tämä käynnistää testifunktion, joka hyväksyy float-argumentin, eli toisen testifunktion.
  15. Kutsu funktiotesti ja anna sille argumentin arvoksi viisi. Tämä käynnistää testifunktion, joka hyväksyy merkkiargumentin, eli kolmannen testifunktion.
  16. Ohjelman on palautettava arvo, jos se toimii onnistuneesti.
  17. Main()-funktion rungon loppu.

Meillä on kolme funktiota, joilla on sama nimi, mutta erityyppiset argumentit. Olemme saavuttaneet polymorfismin.

Operator Ylikuormitus

In Operator Ylikuormitus, määrittelemme uuden merkityksen a:lle C++ operaattori. Se muuttaa myös operaattorin toimintaa. Voimme esimerkiksi määritellä + -operaattorin kahden merkkijonon yhdistämiseksi. Tunnemme sen yhteenlaskuoperaattorina numeeristen arvojen yhteenlaskemiseen. Määritelmämme jälkeen, kun se sijoitetaan kokonaislukujen väliin, se laskee ne yhteen. Kun se sijoitetaan merkkijonojen väliin, se ketjuttaa ne.

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

lähtö:

C++ operaattorin ylikuormitus Esimerkin 2 tuloste

Tässä on kuvakaappaus koodista:

C++ operaattorin ylikuormitus Esimerkki 2 koodi

C++ operaattorin ylikuormitus Esimerkin 2 tulos

Code Selitys:

  1. Sisällytä iostream-otsikkotiedosto ohjelmaamme, jotta voit käyttää sen toimintoja.
  2. Sisällytä std-nimiavaruus ohjelmaamme, jotta voit käyttää sen luokkia kutsumatta sitä.
  3. Luo luokka nimeltä ComplexNum. { merkitsee luokan rungon alkua.
  4. Käytä yksityisen käyttöoikeuden muokkaajaa merkitäksesi muuttujat yksityisiksi, mikä tarkoittaa, että niihin pääsee vain luokasta käsin.
  5. Määritä kaksi kokonaislukumuuttujaa, real ja over.
  6. Käytä public access -modifikaattoria merkitäksesi konstruktorin julkiseksi, mikä tarkoittaa, että se on käytettävissä myös luokan ulkopuolelta.
  7. Luo luokan konstruktori ja alusta muuttujat.
  8. Alusta muuttujan real arvo.
  9. Alusta muuttujan arvo yli.
  10. Rakentajan rungon loppu.
  11. Meidän on ohitettava +-operaattorin merkitys.
  12. Luo tietotyypin tulos, jonka tyyppi on ComplexNum.
  13. Käytä +-operaattoria kompleksilukujen kanssa. Tämä rivi lisää luvun reaaliosan toisen luvun reaaliosaan.
  14. Käytä +-operaattoria kompleksilukujen kanssa. Tämä rivi lisää luvun imaginaarisen osan toisen luvun imaginaariseen osaan.
  15. Ohjelma palauttaa muuttujan tuloksen arvon onnistuneen suorituksen jälkeen.
  16. + -operaattorin uuden merkityksen, eli ylikuormituksen, määritelmän loppu.
  17. Kutsu print()-menetelmää.
  18. Tulosta uusi kompleksiluku lisäyksen jälkeen konsolissa.
  19. Print()-funktion rungon loppu.
  20. ComplexNum-luokan rungon loppu.
  21. Kutsu main()-funktiota.
  22. Välitä sekä todellisten että monimutkaisten lisättävien osien arvot. C1:n ensimmäinen osa lisätään c2:n ensimmäiseen osaan, eli 10+3. C1:n toinen osa lisätään c:n toiseen osaan, eli 2+7.
  23. Suorita operaatio overloaded + -operaattorilla ja tallenna tulos muuttujaan c3.
  24. Tulosta konsoliin muuttujan c3 arvo.
  25. Main()-funktion rungon loppu.

Jos käännösaikainen polymorfismi tekee päätöksen aikaisin, toinen luokka lykkää valintaa suoritukseen asti.

Suorituksen polymorfismi

Tämä tapahtuu, kun objektin menetelmää kutsutaan/kutsutaan ajon aikana eikä käännösajan aikana. Ajonaikainen polymorfismi saavutetaan funktion ohituksella. Kutsuttava/kutsuttava toiminto määritetään ajon aikana.

Toiminnon ohitus

Funktioiden ohittaminen tapahtuu, kun perusluokan funktiolle annetaan uusi määritelmä johdetussa luokassa. Tuolloin voimme sanoa, että perustoiminto on ohitettu.

Esimerkiksi:

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

}

lähtö:

C++ toiminnon ohittava lähtö

Tässä on kuvakaappaus koodista:

C++ funktion ohituskoodi

Code Selitys:

  1. Tuo iostream-otsikkotiedosto ohjelmaamme käyttääksesi sen toimintoja.
  2. Sisällytä std-nimiavaruus ohjelmaamme, jotta voit käyttää sen luokkia kutsumatta sitä.
  3. Luo luokka nimeltä Mammal. { merkitsee luokan rungon alkua.
  4. Käytä julkisen käyttöoikeuden muokkaajaa asettaaksesi luotavan toiminnon julkisesti käytettäväksi. Se on käytettävissä tämän luokan ulkopuolelta.
  5. Luo julkinen toiminto nimeltä eat. { merkitsee funktion rungon alkua.
  6. Tulosta cout-funktioon lisätty käsky, kun funktio eat() kutsutaan.
  7. Toiminnon eat() rungon loppu.
  8. Nisäkäsluokan ruumiin loppu.
  9. Luo luokka nimeltä Lehmä, joka perii nisäkkäiden luokan. Lehmä on johdettu luokka, kun taas Nisäkäs on perusluokka. { merkitsee tämän luokan alkua.
  10. Käytä julkisen käyttöoikeuden muokkaajaa merkitäksesi toiminnon, jonka olemme luomassa, julkisesti käytettäväksi. Se on käytettävissä tämän luokan ulkopuolelta.
  11. Ohita funktio eat(), joka määritettiin perusluokassa. { merkitsee funktion rungon alkua.
  12. Lause, joka tulostetaan konsolissa, kun tämä toiminto vedetään.
  13. Funktion eat() rungon loppu.
  14. Lehmän luokan rungon loppu.
  15. Kutsu main()-funktiota. { merkitsee tämän funktion rungon alkua.
  16. Luo Cow-luokan esiintymä ja anna sille nimi c.
  17. Kutsu Cow-luokassa määriteltyä eat()-funktiota.
  18. Ohjelman on palautettava arvo, kun se on suoritettu onnistuneesti.
  19. Main()-funktion loppu.

C++ Virtuaalinen toiminto

Virtuaalinen funktio on toinen tapa toteuttaa ajonaikaista polymorfismia C++. Se on erityinen funktio, joka on määritelty perusluokassa ja määritelty uudelleen johdetussa luokassa. Virtuaalifunktion ilmoittamiseen tulee käyttää virtuaalista avainsanaa. Avainsanan tulee edeltää funktion ilmoitusta perusluokassa.

Jos virtuaalinen funktioluokka periytyy, virtuaaliluokka määrittelee virtuaalifunktion uudelleen tarpeidensa mukaan. Esimerkiksi:

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

lähtö:

C++ virtuaalifunktion lähtö

Tässä on kuvakaappaus koodista:

C++ virtuaalinen funktiokoodi

Code Selitys:

  1. Sisällytä iostream-otsikkotiedosto koodiin käyttääksesi sen toimintoja.
  2. Sisällytä std-nimiavaruus koodiimme käyttääksesi sen luokkia kutsumatta sitä.
  3. Luo luokka nimeltä ClassA.
  4. Käytä julkisen käyttöoikeuden muokkaajaa merkitäksesi luokan jäsen julkisesti käytettäväksi.
  5. Luo virtuaalinen funktio nimeltä show(). Siitä tulee julkinen tilaisuus.
  6. Teksti, joka tulostetaan, kun show()-toimintoa kutsutaan. Loppu on a C++ avainsana, joka tarkoittaa loppuriviä. Se siirtää hiiren osoittimen seuraavalle riville.
  7. Virtuaalifunktion show() rungon loppu.
  8. Luokan A rungon loppu.
  9. Luodaan uusi luokka nimeltä ClassB, joka perii luokan ClassA. Luokasta A tulee perusluokka, kun taas luokasta B tulee johdettu luokka.
  10. Käytä julkisen käyttöoikeuden muokkaajaa merkitäksesi luokan jäsen julkisesti käytettäväksi.
  11. Määritä perusluokassa johdettu virtuaalifunktio show() uudelleen.
  12. Konsolille tulostettava teksti, kun vedetyssä luokassa määritetty show()-funktio kutsutaan.
  13. Show()-funktion rungon loppu.
  14. Johdetun luokan, ClassB, rungon loppu.
  15. Kutsu main()-funktiota. Ohjelmalogiikka tulisi lisätä sen runkoon.
  16. Luo osoitinmuuttuja nimeltä a. Se osoittaa luokkaan nimeltä ClassA.
  17. Luo esiintymä luokasta nimeltä ClassB. Esiintymälle annetaan nimi b.
  18. Määritä muuttujan a osoitteen b arvovarastot.
  19. Kutsua johdetussa luokassa määritetty show()-funktio. Myöhäinen sidonta on otettu käyttöön.
  20. Main()-funktion rungon loppu.

Alla oleva taulukko vertailee molempia mekanismeja rinnakkain.

Käännösajan polymorfismi vs. Ajonaikainen polymorfismi

Tässä ovat tärkeimmät erot näiden kahden välillä:

Kokoonpanoaikainen polymorfismi Ajonaikainen polymorfismi
Sitä kutsutaan myös varhaiseksi sitoutumiseksi tai staattiseksi polymorfismiksi Sitä kutsutaan myös myöhäiseksi/dynaamiseksi sitoutumiseksi tai dynaamiseksi polymorfismiksi.
Menetelmää kutsutaan/kutsutaan käännösaikana Menetelmää kutsutaan/kutsutaan ajon aikana
Toteutettu toimintojen ylikuormituksen ja käyttäjän ylikuormituksen kautta Toteutettu menetelmän ohituksen ja virtuaalisten toimintojen kautta
Esimerkki, menetelmän ylikuormitus. Monilla menetelmillä voi olla samanlaiset nimet, mutta eri määrä tai tyyppisiä argumentteja Esimerkki, menetelmän ohittaminen. Monilla menetelmillä voi olla samanlainen nimi ja sama prototyyppi.
Nopeampi suoritus, koska metodin löytäminen tehdään käännösaikana Hitaampi suoritus, koska metodin etsintä tehdään ajon aikana.
Less Joustavuutta ongelmanratkaisuun tarjotaan, koska kaikki on tiedossa käännösaikana. Monimutkaisten ongelmien ratkaisemiseen tarjotaan paljon joustavuutta, koska menetelmät löydetään ajon aikana.

UKK

Ylikuormitus määrittelee useita funktioita samalla nimellä, mutta eri parametreilla, jotka ratkaistaan ​​käännösaikana ilman periytymistä. Ohittaminen määrittelee uudelleen perityn perusluokan funktion, jolla on sama allekirjoitus johdetussa luokassa, ja tämä ratkaistaan ​​ajonaikana, kun funktio julistetaan virtuaaliseksi.

Puhdas virtuaalifunktio deklaroidaan arvolla = 0, eikä sillä ole runkoa perusluokassa. Mikä tahansa luokka, joka sisältää sellaisen, muuttuu abs-funktioksi.tract-luokka, jota ei voida instansoida suoraan ja joka pakottaa johdetut luokat tarjoamaan oman toteutuksensa.

Kun luokka määrittelee virtuaalifunktioita, kääntäjä rakentaa funktio-osoittimista virtuaalitaulukon (vtable). Jokainen objekti tallentaa piilotetun osoittimen vtable-luokkaansa, joten perusluokan osoitin kutsuu oikeaa ohitettua funktiota ajonaikana myöhäisen sidonnan avulla.

Kyllä. Mallit antavat yhden funktion tai luokan käsitellä useita tietotyyppejä, ja kääntäjä luo tyyppikohtaista koodia käännöksen aikana. Tämä geneerinen ohjelmointi on staattisen polymorfismin muoto, joka täydentää funktioiden ja operaattoreiden ylikuormitusta.

Kun poistat johdetun objektin perusluokan osoittimen avulla, vain virtuaalinen destructor takaa, että myös johdettu destructor toimii. Ilman sitä siivous on epätäydellinen ja aiheuttaa muistivuotoja. Perusluokan destructorin virtuaaliseksi julistaminen saa tuhoamisen toimimaan polymorfisesti.

Polymorfismi antaa yhden käyttöliittymän käsitellä useita objektityyppejä, mikä vähentää päällekkäistä koodia ja yksinkertaistaa ylläpitoa. Se tukee joustavia ja laajennettavia malleja, joissa uudet johdetut luokat voidaan liittää muuttamatta olemassa olevaa koodia, ja se mahdollistaa tehokkaiden mallien rakentamisen perusluokkaosoittimien ja viittausten varaan.

Kyllä. Tekoälykoodausavustajat muuttavat lyhyen kommentin ylikuormitetuiksi funktioiksi, virtuaalisiksi perusluokiksi ja johdetuiksi ohituksiksi. Ne käsittelevät toistuvat mallikoodit nopeasti, vaikka virtuaaliset avainsanat, funktioiden allekirjoitukset ja destructor-turvallisuus tulisi silti tarkistaa ennen luodun koodin kääntämistä.

Kyllä. GitHub Copilot ehdottaa virtuaalifunktioiden määrittelyjä, johdettujen luokkien ohituksia ja operaattorien ylikuormitusta kirjoittaessasi. Se nopeuttaa periytymishierarkioiden mallipohjaista käsittelyä, vaikka sinun tulisi vahvistaa virtuaalinen avainsana, vastaavat allekirjoitukset ja korjata myöhäinen sidonta ennen rakentamista.

Tiivistä tämä viesti seuraavasti: