Föreläsning 4
Arv bakgrund (kap. 9) Hund pälsfärg favoritben smutsa_ned() ät_katt() Kanin pälsfärg slaktvikt smutsa_ned() Katt pälsfärg smutsa_ned() klös_soffa()
Arv bakgrund Många klasser delar på egenskaper och beteende Det framstår onödigt att återskapa samma saker om och om igen C++ erbjuder verktyg för att samla ihop gemensamma egenskaper och beteenden i överklasser
Problemlösningsmetodik Börja med att identifiera alla klasser som behövs Leta sedan efter beteenden som är gemensamma för flera klasser Bryt ut i en överklass Kontrollera även om det redan finns passande klasser
Lösning Husdjur pälsfärg smutsa ned() Hund favoritben ät katt() Kanin slaktvikt Katt klössoffa()
Implikationer av arv Underklassen är en utökad variant av överklassen Underklassen klarar minst allt vad överklassen klarar, oftast mer Underklassen innehåller garanterat all den data som finns i överklassen, dessutom kan extra data finnas
Implikationer av arv Underklassens beteende är mer specialiserat Överklassens beteende är mer generellt Klasser som är mycket generaliserade är ofta abstrakta och kan inte instansieras Husdjur Fordon
Vad ärvs? Medlemsfunktioner ärvs Datamedlemmar ärvs Friends (vänner) ärvs inte Konstruktor ärvs inte Basklassens (överklassen) konstruktor anropas via initieringslistan
Fordon class Fordon { public: Fordon(Person & owner) : m_owner(owner) {} Point GetPosition() { return m_position; } Person & GetOwner() { return m_owner; } protected: Point m_position; Person & m_owner; }; // Fordon initierar owner & position i sin // initieringslista Fordon(Person & owner) : m_position(10,5), m_owner(owner) { }
Bil ärver från Fordon // Ärver från Fordon class Bil : public Fordon { public: Bil(Person & owner); void TurnWheel(float wheel); protected: float m_wheelpos; } // Bilkonstruktorn anropar överklassens konstr. // OBS likheten med initieringslista av variabler Bil::Bil(Person & owner) : Fordon(owner) { m_wheelpos = 0; }
Alltså är en Bil ett Fordon // vi kommer åt alla Fordons medlemsvariabler void Bil::TurnWheel(float wheel) { m_wheelpos = wheel; int x = GetPosition().GetX(); } // vi kan använda m_owner som är deklarerad // i klassen Fordon cout << Bilen med ägare << m_owner.name() << svänger med ratten << endl;
Motorcykel ärver från Fordon // Ärver från Fordon class Motorcykel : public Fordon { public: Motorcykel(Person & owner); void SetColor(int color) { m_color = color; } int GetColor() { return m_color; } protected: int m_color; } // Konstruktorn anropar överklassens Fordon. Motorcykel::Motorcykel(Person & owner) : Fordon(owner) { m_color = 0; }
Gemensamt mellan Motorcykel och Bil Motorcykel och bil har endast Fordon gemensamt Inga andra egenskaper delas mellan objekten Klasser byggs upp i trädstruktur Underklasser är mer specialiserade Underklasserna erbjuder samma gränssnitt som överklasserna
Metodersättning En underklass kan ersätta en metod i överklassen genom att definiera om den i underklassen Överklassens ursprungliga metod kan anropas från underklassen med operatorn :: Exempel: Fordon::GetPosition()
Synligt arv (public) (kap. 9.3) Synliga (public) medlemmar i överklassen blir synliga i underklassen Skyddade (protected) medlemmar i överklassen blir skyddade i underklassen Privata (private) medlemmar i överklassen kommer man inte åt från underklassen
Skyddat arv (protected) Synliga (public) medlemmar i överklassen blir skyddade (protected) i underklassen Skyddade (protected) medlemmar i överklassen blir skyddade i underklassen Privata (private) medlemmar i överklassen kommer man inte åt från underklassen
Privat arv (private) Synliga (public) och Skyddade (protected) medlemmar i överklassen blir privata (private) i underklassen Privata (private) medlemmar i överklassen kommer man inte åt från underklassen
Tillgänglighet översikt i överklass typ av arv i underklass public public public protected public protected private public ej åtkomlig public protected protected protected protected protected private protected ej åtkomlig public private private protected private private private private ej åtkomlig
Minneslayout Nya datamedlemmar läggs till i slutet av klassen, ärvda medlemmar ligger alltid på samma offset i minnet Fordon Person & ägare Point position Bil Person & ägare Point position float wheelpos Motorcykel Person & ägare Point position int color
Statisk allokering! Ett statiskt allokerat objekt har en bestämd storlek reserverat i minnet Ett underklass-objekt kräver mer lagringsutrymme än överklassobjekten för att få rum med de extra datamedlemmarna Slicing uppstår om ett objekt av en överklass tilldelas ett objekt av en underklass All data i underklassen får inte rum, och förkastas därför Undvik genom att använda pekare eller referenser
Exempel (slicing) Person owner( kalle ); Bil b(owner); Fordon f = b; // SLICING!!! Fordon &f = *b; // OK
Exempel (tillgänglighet vid arv) Privat eller skyddat arv class Stack : private Vector { public: void push(elem e) {/* peta i vektorn */} Elem pop() {/* peta i vektorn */} }; // main... Stack s; Elem e,e1,e2; s.push(e1); s.push(e2); e=teststack.pop(); Vad händer om man använder public inheritance istället? Vi får ett väldigt fult anropssnitt! t=teststack[10]; teststack.push_back(e);
en stack är inte en vektor Snyggare och bättre med en vektor som beståndsdel (aggregat) istället för arv class Stack { public: push(elem e) { thestack.pushback(e); } Elem pop() { /*... */ } private: Vector thestack; };
Konstruktor (kap. 9.2) När ett objekt skapas sker följande Konstruktorn för ev. överklass anropas alltid först Konstruktorerna för den aktuella klassens datamedlemmar anropas Satserna i den aktuella klassens konstruktor exekveras
Konstruktoranrop Konstruktorn för en överklass kan anropas via underklassens konstruktor Bil::Bil(Person & owner) : Fordon(owner) Om ingen konstruktor för överklassen specificeras anropas överklassens defaultkonstruktor
Destruktor När ett objekt tas bort sker följande Satserna i den aktuella klassens destruktor exekveras Destruktorerna för den aktuella klassens datamedlemmar anropas Destruktorn för ev. överklass anropas
Konstigheter vid arv Funktionsöverlagring Metoder deklarerade i en subklass döljer alla metoder med samma namn i basklassen, oavsett vilka parametrar metoderna har
Arv fördelar (kap. 9.4) Arv kan som vi har sett underlätta programmering genom att olika klasser kan dela på gemensam källkod Arv för även med sig andra fördelar. När ett objekt ärver från en överklass garanterar vi att underklassen klarar allt som överklassen klarar
Användning av arv // skriv ut information om ett fordon void Fordonsinfo(Fordon & fordon) { // alla fordon har en ägare Person & owner = fordon.getowner(); } cout << Fordonet har en ägare vid namn << owner.name(); Person owner1( kalle ); Person owner2( olle ); Bil b(owner1); Motorcykel m(owner2); // vi kan behandla m och b som Fordon Fordonsinfo(b); // skriver ut kalle Fordonsinfo(m); // skriver ut olle
Multipelt arv (kap. 9.10) Klasser kan ärva från mer än en klass För varje klass specificeras om arvet är public, protected eller private Används för att kombinera egenskaper från två eller flera klasser
Exempel (multipelt arv) Amfibiebil ärver alla egenskaper som bil och båt har En Amfibiebil är en Bil och en Båt Bil Båt Amfibiebil
Namnkonflikter När klasser ärver från flera överklasser kan namnkonflikter uppstå Exempel: om både bil och båt har implementerat två metoder som heter Print Namnkonflikterna löses genom att ersätta metoden Print i underklassen och specificera vilken metod i överklassen som skall anropas. Detta löses med exempelvis Bil::Print. Eventuellt kan bägge överklassernas metoder anropas efter varandra
Exempel (Amfibiebil) class Bil { public: Bil(int age) { m_age = age; } void Print() { cout << Bil, årsmodell << m_model; } protected: int m_model; }; class Bat { public: Bat(int length) { m_length = length; } void Print() { cout << Båt, längd << m_length; } protected: int m_length; };
Exempel (Amfibiebil) forts. class Amfibiebil : public Bil, public Bat { public: Amfibiebil(int age, int length) : Bil(age), Bat(length) {} // för att undvika namnkonflikter måste vi // ersätta metoden Print void Print() { // skriv först ut bilinformationen Bil::Print(); // sedan båtinformationen Bat::Print(); } };
Exempel (multipelt arv) Fordon Bil Båt Amfibiebil
Konstigheter med multipelt arv Multipelt arv kan medföra problem i vissa fall Om en klass ärver från två klasser som har en gemensam överklass får underklassen dubblerade data från den gemensamma överklassen Namnkonflikter uppstår för metoder från den gemensamma överklassen Undvik multipelt arv