Multipel arv i C ++ är kraftfullt, men ett knepigt verktyg, som ofta leder till problem om det inte används noggrant - problem som Diamond Problem.

I den här artikeln kommer vi att diskutera diamantproblemet, hur det härrör från flera arv och vad du kan göra för att lösa problemet.

Multipel arv i C ++

Multipel arv är en funktion i objektorienterad programmering (OOP) där en underklass kan ärva från mer än en superklass. Med andra ord kan en barnklass ha mer än en förälder.

Figuren nedan visar en bild av flera arv.

I diagrammet ovan, klass C har klass A och klass B som dess föräldrar.

Om vi ​​överväger ett verkligt scenario, ärver ett barn från sin far och mamma. Så ett barn kan representeras som en härledd klass med "Far" och "Mor" som sina föräldrar. På samma sätt kan vi ha många sådana verkliga exempel på flera arv.

I multipel arv körs konstruktörerna av en ärvd klass i den ordning de ärvs. Å andra sidan avrättas destruktorer i omvänd ordning efter sitt arv.

Låt oss nu illustrera det multipla arvet och verifiera ordningen för konstruktion och förstörelse av objekt.

instagram viewer

Kodillustration av flera arv

För illustrationen med flera arv har vi exakt programmerat ovanstående representation i C ++. Koden för programmet ges nedan.

#omfatta
med namnutrymme std;
klass A // basklass A med konstruktör och destruktor
{
offentlig:
A () {cout << "klass A:: Constructor" << endl; }
~ A () {cout << "klass A:: Destructor" << endl; }
};
klass B // basklass B med konstruktor och destruktor
{
offentlig:
B () {cout << "klass B:: Constructor" << endl; }
~ B () {cout << "klass B:: Destructor" << endl; }
};
klass C: offentlig B, offentlig A // härledd klass C ärver klass A och sedan klass B (notera ordningen)
{
offentlig:
C () {cout << "klass C:: Constructor" << endl; }
~ C () {cout << "klass C:: Destructor" << endl; }
};
int main () {
C c;
returnera 0;
}

Utmatningen vi får från ovanstående program är följande:

klass B:: Konstruktör
klass A:: Konstruktör
klass C:: Konstruktör
klass C:: Destructor
klass A:: Destructor
klass B:: Destructor

Om vi ​​nu kontrollerar utmatningen ser vi att konstruktörerna kallas i ordning B, A och C medan destruktorerna är i omvänd ordning. Nu när vi känner till grunderna för flera arv, fortsätter vi att diskutera diamantproblemet.

Diamantproblemet, förklarat

Diamantproblemet uppstår när en barnklass ärver från två föräldraklasser som båda delar en gemensam morföräldraklass. Detta illustreras i diagrammet nedan:

Här har vi en klass Barn ärver från klasser Far och Mor. Dessa två klasser ärver i sin tur klassen Person eftersom både far och mor är person.

Som framgår av figuren ärver klass Barn egenskaperna hos klassperson två gånger - en gång från far och igen från mor. Detta ger upphov till oklarhet eftersom kompilatorn inte förstår vilken väg att gå.

Detta scenario ger upphov till ett diamantformat arvsdiagram och kallas berömt "Diamantproblemet."

Kodillustration av diamantproblemet

Nedan har vi representerat ovanstående exempel på diamantformat arv programmatiskt. Koden ges nedan:

#omfatta
med namnutrymme std;
klassperson {// klassperson
offentlig:
Person (int x) {cout << "Person:: Person (int) kallad" << endl; }
};
klass Fader: offentlig person {// klass Fader ärver Person
offentlig:
Far (int x): Person (x) {
cout << "Far:: Far (int) kallad" << endl;
}
};
klass Mor: offentlig person {// klass Mor ärver person
offentlig:
Mor (int x): Person (x) {
cout << "Mor:: Mor (int) kallad" << endl;
}
};
klass Barn: offentlig far, offentlig mamma {// Barn ärver far och mor
offentlig:
Barn (int x): Mor (x), Far (x) {
cout << "Barn:: Barn (int) kallat" << endl;
}
};
int main () {
Barnbarn (30);
}

Följande är resultatet av detta program:

Person:: Person (int) uppringd
Far:: Far (int) ringde
Person:: Person (int) uppringd
Mor:: Mor (int) ringde
Barn:: Barn (int) kallas

Nu kan du se oklarheten här. Personklasskonstruktören kallas två gånger: en gång när faderklassobjektet skapas och nästa när moderklassobjektet skapas. Personklassens egenskaper ärvs två gånger, vilket ger upphov till oklarhet.

Eftersom personklasskonstruktorn kallas två gånger kommer destruktorn också att kallas två gånger när objektet i barnklassen förstörs.

Om du nu har förstått problemet korrekt, låt oss diskutera lösningen på diamantproblemet.

Hur man löser diamantproblemet i C ++

Lösningen på diamantproblemet är att använda virtuell nyckelord. Vi gör de två föräldraklasserna (som ärver från samma morföräldrarklass) till virtuella klasser för att undvika två kopior av morföräldrarklassen i barnklassen.

Låt oss ändra ovanstående illustration och kontrollera utgången:

Kodillustration för att lösa diamantproblemet

#omfatta
med namnutrymme std;
klassperson {// klassperson
offentlig:
Person () {cout << "Person:: Person () som heter" << endl; } // Baskonstruktör
Person (int x) {cout << "Person:: Person (int) kallad" << endl; }
};
klass Far: virtuell offentlig person {// klass Far ärver Person
offentlig:
Far (int x): Person (x) {
cout << "Far:: Far (int) kallad" << endl;
}
};
klass Mamma: virtuell offentlig person {// klass Mamma ärver Person
offentlig:
Mor (int x): Person (x) {
cout << "Mor:: Mor (int) kallad" << endl;
}
};
klass Barn: offentlig far, offentlig mamma {// klass Barn ärver far och mor
offentlig:
Barn (int x): Mor (x), Far (x) {
cout << "Barn:: Barn (int) kallat" << endl;
}
};
int main () {
Barnbarn (30);
}

Här har vi använt virtuell nyckelord när klasserna Far och mor ärver personklassen. Detta kallas vanligtvis "virtuellt arv", vilket garanterar att endast en enda instans av den ärvda klassen (i det här fallet personklassen) vidarebefordras.

Med andra ord kommer barnklassen att ha en enda instans av personklassen, delad av både fadern och moderklassen. Genom att ha en enda instans av personklassen löses oklarheten.

Utmatningen av ovanstående kod ges nedan:

Person:: Person () uppringd
Far:: Far (int) ringde
Mor:: Mor (int) ringde
Barn:: Barn (int) kallas

Här kan du se att klassens personkonstruktör endast kallas en gång.

En sak att notera om virtuellt arv är att även om den parametrerade konstruktören av Personklass kallas uttryckligen av far och mor klasskonstruktörer genom initialisering listor, endast baskonstruktören för personklassen kommer att kallas.

Detta beror på att det bara finns en enda instans av en virtuell basklass som delas av flera klasser som ärver från den.

För att förhindra att baskonstruktorn körs flera gånger anropas inte konstruktorn för en virtuell basklass av klassen som ärver från den. Istället kallas konstruktören av konstruktören av betongklassen.

I exemplet ovan anropar klassen Child direkt baskonstruktorn för klasspersonen.

Relaterad: En nybörjarguide till standardmallbiblioteket i C ++

Vad händer om du behöver köra den parametriserade konstruktorn för basklassen? Du kan göra det genom att uttryckligen kalla det i barnklassen snarare än fadern eller moderklassen.

Diamantproblemet i C ++, löst

Diamantproblemet är en oklarhet som uppstår i flera arv när två föräldraklasser ärver från samma morförälderklass, och båda föräldraklasser ärvs av en enda barnklass. Utan att använda virtuellt arv skulle barnklassen ärva egenskaperna hos morförälderklassen två gånger, vilket leder till oklarhet.

Detta kan dyka upp ofta i verkliga koder, så det är viktigt att ta itu med den oklarheten när det upptäcks.

Diamantproblemet åtgärdas med hjälp av virtuellt arv, där virtuell nyckelordet används när föräldraklasser ärver från en delad morförälderklass. Genom att göra det görs bara en kopia av morföräldrarklassen, och föräldraklassens objektkonstruktion görs av barnklassen.

Dela med sigTweetE-post
De 10 bästa nybörjarprojekten för nya programmerare

Vill du lära dig programmering men vet inte var du ska börja? Dessa nybörjarprogrammeringsprojekt och självstudier kommer att starta dig.

Läs Nästa

Relaterade ämnen
  • Programmering
  • C Programmering
Om författaren
MUO -personal

Prenumerera på vårt nyhetsbrev

Gå med i vårt nyhetsbrev för tekniska tips, recensioner, gratis e -böcker och exklusiva erbjudanden!

Klicka här för att prenumerera