5 Arv och dynamisk bindning FIGUR

Relevanta dokument
Introduktion till arv

Programmering i C++ EDA623 Arv. EDA623 (Föreläsning 6) HT / 42

Introduktion. Klasser. TDP004 Objektorienterad Programmering Fö 2 Objektorientering grunder

TDDC76 - Programmering och Datastrukturer

Objektorientering - Arv och polymorfi. Eric Elfving Institutionen för datavetenskap

Dynamisk bindning och polymorfism

Tentamen i Objektorienterad Programmering 5p, Au, D, Fri, Pr,

Innehåll. 1 Kort om dynamisk polymorfism. 2 Arv i C++ 3 Multipelt arv. 4 Något om statisk polymorfism. class Container {

TDIU01 Programmering i C++

Arv. Objektorienterad och komponentbaserad programmering

Programmering i C++ EDA623 Objektorienterad programutveckling. EDA623 (Föreläsning 5) HT / 33

2I1049 Föreläsning 5. Objektorientering. Objektorientering. Klasserna ordnas i en hierarki som motsvarar deras inbördes ordning

Innehåll. Konstruktorer vid arv Regler för basklassens konstruktor. Konstruktorer vid arv. Konstruktorer vid arv. Konstruktorer vid arv

TDDC76 - Programmering och Datastrukturer

3 Klasser och objekt

1 Objektorienterad programkonstruktion

TDIU01 - Programmering i C++, grundkurs

KLASSER. Inkapsling Abstrakt datatyp Public och private. Klassmedlemmar Datamedlemmar Exempel Funktionsmedlemmar

TDDC76 - Programmering och Datastrukturer

Innehåll. Pekaren this Självreferens. Klasser Resurshantering, representation. Överlagring av operatorer. Överlagring av operatorer

Arv bakgrund (kap. 9)

Programsystem konstruktion med C++ (2D1387) Innehåll. övning 2 klasser och arv

Klasser. Kapitel 2. Kapitel 2 - Klasser, medlemmar och arv. Klasser. Klasser Medlemmar Arv

ÖREBRO UNIVERSITET. Lösningarna till tentamensuppgifterna sätts ut på kurssidan på nätet i dag kl 13.

Del2 Klasser, medlemmar och arv Ämnesområden denna föreläsning:

Del3 Klassanvändning, operatorer och pekare Ämnesområden denna föreläsning:

TDP004. Minne och pekare. Eric Elfving Institutionen för datavetenskap

TDDC76 - Programmering och Datastrukturer

Tentamen i TDP004 Objektorienterad Programmering Lösningsförslag

OOP Objekt-orienterad programmering

Programsystemkonstruktion med C++

Nedan skapar vi klassen Person innehållande datamedlemmar för förnamn, efternamn, ålder, längd och vikt:

TENTAMEN CD5250. Objektorienterad programutveckling med C++, 5p. Datum: , Tid: 14:00-19:00

Vad är en klass? Övning 2. Konstruktorer. ffl copy-konstruktorn tar en instans av klassen som. ffl default-konstruktorn tar inga argument och kan

Föreläsning 5-6 Innehåll. Exempel på program med objekt. Exempel: kvadratobjekt. Objekt. Skapa och använda objekt Skriva egna klasser

DD2387 Programsystemkonstruktion med C++ Tentamen 2

Högskolan Dalarna sid 1 av 7 DI-institutionen Hans-Edy Mårtensson Sten Sundin

DD2387 Programsystemkonstruktion med C++ Tentamen 1 Torsdag 7 januari 2016, 14:00-18:00

TDDC76 Programmering och datastrukturer

Föreläsning 5-6 Innehåll

Innehåll. Typomvandlingar (casting) Implicita Typomvandlingar. Typomvandlingar (casting) Implicita Typomvandlingar

TDDC76 - Programmering och Datastrukturer

allokeras på stacken dynamiskt new delete

Minneshantering. Minneshantering. Minneshantering. Undvik pekare

Klasshierarkier - repetition

C++ Objektorientering - Klasser. Eric Elfving

Det objektorienterade synsättet. Objekt. Datorprogrammet kan uppfattas som en slags modell av den verklighet programmet skall samverka med.

Programmering B med Visual C

Grundläggande programmering, STS 1, VT Sven Sandberg. Föreläsning 14

Polymorfi. Objektorienterad och komponentbaserad programmering

Föreläsning 8. Arv. Arv (forts) Arv och abstrakta klasser

C++ Objektorientering - Klasser. Eric Elfving Institutionen för datavetenskap

Klasser. Det är egentligen nu som kursen i programmeringsteknik börjar..., s k objektorienterad programmering.

Laboration 1: Figurer i hierarki

1 Klasser och objektorientering Vad är objektorientering?

Kapitel 3. Synlighet. Kapitel 3 - Klassanvändning, operatorer och pekare. Synlighet

Subklasser och arv Inledning till grafik (JFrame och JPanel). Något om interface. Objektorienterad programvaruutveckling GU (DIT011) Subklasser

Tentamen i TDP004 Objektorienterad Programmering Teoretisk del

TDIU20 - Objektorienterad programmering i c++ - föreläsning 4

Programsystemkonstruktion med C++: Övning 2. Karl Palmskog september 2010

ÖREBRO UNIVERSITET. Lösningarna till tentamensuppgifterna sätts ut på kurssidan på nätet i dag kl 19.

För alla uppgifter på tentan gäller: Man får använda både standard-c++ (som till exempel har pekare som anges med * och objekt som skapas med new) och

Arv. Fundamental objekt-orienterad teknik. arv i Java modifieraren protected Lägga till och modifiera metoder med hjälp av arv Klass hierarkier

Föreläsning 13 Innehåll

Målen med OOSU. Objektorienterad programmering. Objektorienterad programmering. Karlstads Universitet, Johan Öfverberg 1

Innehåll. Introduktion till objektorientering. OOP (objektorienterad programmering) Objekt, instanser, klasser

Högskolan Dalarna sid 1 av 5 Data-avdelningen Hans-Edy Mårtensson

TDIU20 - Objektorienterad programmering i c++ - föreläsning 6

Övning 4. Arv och andra relationer

Collections Collections "Envisa" objekt Klasserna Bofstream och Bifstream Definition av metoder i klasserna Bifstream och Bofstream Klassen Streng

Innehåll. Typomvandlingar (casting) Implicita Typomvandlingar. Typomvandlingar (type casts) Explicita, namngivna typomvandlingar (C++-11)

Skapa, kopiera och destruera klassobjekt

TDDE10 m.fl. Objektorienterad programmering i Java Föreläsning 6 Erik Nilsson, Institutionen för Datavetenskap, LiU

LÖSNINGSFÖRSLAG Programmeringsteknik För Ing. - Java, 5p

F8 - Arv. ID1004 Objektorienterad programmering Fredrik Kilander

Programmering i C++ En manual för kursen Datavetenskaplig introduktionskurs 5p

Övriga byggstenar. Övriga byggstenar. Några tips under programutveckling. Beroenden Pekare till funktioner Typkonvertering

LÖSNINGSFÖRSLAG TILL Tentamen i objektorienterad programmering i C++ I

Objektorienterad programmering Föreläsning 12. Copyright Mahmud Al Hakim

TDDC76 - Programmering och Datastrukturer

EDAA20 Programmering och databaser. Mål komprimerat se kursplanen för detaljer. Checklista. Föreläsning 1-2 Innehåll. Programmering.

TDIU01 - Programmering i C++, grundkurs

JAVA Mer om klasser och objektorientering

Det finns många flaggor till g++,

UML. Översikt UML. Relationer mellan klasser. A är ett aggregerat av B:n. Kontor aggregat av Enheter. 12 olika diagramtyper, bl.a.

Synlighet. Namespace Scope-operatorn Klasser Vänner

Objektorienterade programmeringsspråk. Objektorienterade språk. Den objekt-orienterade modellen. Jämför med icke-oo

Objektorienterad programmering

Idag. Javas datatyper, arrayer, referenssemantik. Arv, polymorfi, typregler, typkonvertering. Tänker inte säga nåt om det som är likadant som i C.

Classes och Interfaces, Objects och References, Initialization

Objektorienterad Programmering (OOP) Murach s: kap 12-16

C++ Funktioner 1. int summa( int a, int b) //funktionshuvud { return a+b; //funktionskropp } Värmdö Gymnasium Programmering B ++ Datainstitutionen

Föreläsning 4. Klass. Klassdeklaration. Klasser Och Objekt

Pekare. Pekare. Varför använder vi pekare? Vad är en pekare? Pekare. Deklaration/initiering av pekare

Föreläsning 2 Objektorienterad programmering DD1332. Typomvandling

Problemet. Vi har sett att vi kan ersätta de metoder vi ärver från överklassen med egen funktionalitet (polymorfism)

Innehåll. Pekare Exempel

Arv innebär att man skapar en ny klass (subklass) utifrån en redan existerande klass (superklass, basklass).

Innehåll. Resurshantering. Resource handles. Minnesallokering. Minnesallokering Exempel: allokering på stacken. 6. Resurshantering

JAVAUTVECKLING LEKTION 11

Transkript:

5 Arv och dynamisk bindning Arv är en av hörnstenarna i objektorienterad programmering. Med hjälp av arv kan man skapa underhållsvänliga och förändringsvänliga system. Att hitta arvsrelationer är en viktig del av systemarbetet. Arvsrelationer kan vara både specialiseringar och generaliseringar. Specialisering innebär att man från en basklass hittar subklasser. Generalisering innebär att man samlar ihop subklasser till en basklass. Arvsrelationen kontrolleras med frågan 'är en'. Elefant är ett däggdjur. Dialogfönster är ett fönster osv. Arv är ett steg mot att programmera med återanvändbara komponenter. Man har en färdig fönsterklass (komponent) som man själv kan modifiera till en önskad subfönsterklass genom att lägga till data och funktioner eller förändra funktionaliteten genom att överlagra funktioner. Ex : En rektangel är en figur och en cirkel är en figur. Ska man använda både rektanglar och cirklar i sitt system är det lämpligt att generalisera och använda en basklass figur som håller reda på sådant som är gemensamt, som exempelvis namnet medan subklasserna håller reda på det som är speciellt, exempelvis sidornas längder för rektangeln och radiens längd för cirkeln. FIGUR CIRKEL REKTANGEL // specifikation av klassen FIGUR -- figur.h #ifndef FIGURH #define FIGURH class FIGUR char namn[20]; FIGUR(char *namn = ""); ~FIGUR(); void skriv(); ; #endif 83

OBS! Villkorlig kompilering för att undvika kompileringsfel på grund av dubbla definitioner av klassen FIGUR eftersom figur.h kommer att inkluderas av både rektang.h och cirkel.h. // implementation av klassen FIGUR -- figur.cpp #include "figur.h" #include <cstring> #include <iostream> using namespace std; FIGUR::FIGUR(char *namn) strcpy(this->namn, namn); FIGUR::~FIGUR() void FIGUR::skriv() cout << endl << "Figurnamn : " << namn << endl; // specifikation av klassen REKTANGEL -- rektang.h #include "figur.h" class REKTANGEL : public FIGUR // OBS! Ärver FIGUR float s1, s2; REKTANGEL(char *namn, float s1, float s2); ~REKTANGEL(); float area(); void skriv(); ; 84

// implementation av klassen REKTANGEL -- rektang.cpp #include "rektang.h" #include <iostream> using namespace std; REKTANGEL::REKTANGEL(char *namn, float s1, float s2) :FIGUR(namn) // OBS! Anropar baskonstruktor this->s1 = s1; this->s2 = s2; REKTANGEL::~REKTANGEL() float REKTANGEL::area() return s1*s2; void REKTANGEL::skriv() FIGUR::skriv(); cout << "Sida 1 : " << s1 << endl; cout << "Sida 2 : " << s2 << endl; // specifikation av klassen CIRKEL -- cirkel.h #include "figur.h" class CIRKEL :public FIGUR float r; CIRKEL(char *namn, float r); ~CIRKEL(); float area(); void skriv(); ; // OBS! Ärver FIGUR 85

// implementation av klassen CIRKEL -- cirkel.cpp #include "cirkel.h" #include <iostream> using namespace std; CIRKEL::CIRKEL(char *namn, float r) :FIGUR(namn) // OBS! Anropar baskonstruktor this->r = r; CIRKEL::~CIRKEL() float CIRKEL::area() const float pi = 3.14159; return r*r*pi; void CIRKEL::skriv() FIGUR::skriv(); cout << "Radie : " << r << endl; OBS! REKTANGEL-klassen och CIRKEL-klassen ärver medlemsvariabeln namn från basklassen. Objekt av klassen CIRKEL kommer alltså att ha värden på både namn och radie. Även om CIRKEL ärver namn från basklassen kan inte CIRKEL använda namn direkt vid exempelvis utskriften. Även subklasser måste gå via de publika medlemsfunktionerna. Vill man ge subklasser tillåtelse att använda medlemsvariabler direkt ska man definiera dessa protected istället för private i basklassen. Medlemmar som är protected kan refereras direkt av subklasser men ej av andra. OBS! Medlemsfunktionen skriv ärvs också av subklasserna men här ändras funktionaliteten genom att funktionen omdefinieras (överlagras) i subklassen. Då skriv anropas av CIRKEL-objektet kommer denna överlagrade funktion att användas. OBS! Basklassens konstruktor anropas alltid före subklassens. Parametrar till basklassens konstruktor måste överföras via subklassens konstruktor (jfr medlemsobjekt). Basklassens destruktor anropas alltid efter subklassens. Här finns inga problem med parametrar då destruktorn ej får ha några. 86

// huvudprogram -- figurmai.cpp #include "cirkel.h" #include "rektang.h" void main() CIRKEL cir("cirkel", 1.0); REKTANGEL rek("rektangel", 2.0, 4.5); FIGUR fig, *fp; cir.skriv(); cout << "Area : " << cir.area() << endl; rek.skriv(); cout << "Area : " << rek.area() << endl; fig = cir; fig.skriv(); fp = &cir; fp->skriv(); Utskriften blir : Figurnamn : Cirkel Radie : 1 Area : 3.14159 Figurnamn : Rektangel Sida1 : 2 Sida2 : 4.5 Area : 9 Figurnamn : Cirkel Figurnamn : Cirkel OBS! Det går att tilldela objektet cir av subklassen CIRKEL till objektet fig av basklassen FIGUR, men ej tvärtom. Vid tilldelningen tappar man bort radie eftersom objekt av klassen FIGUR ej har någon sådan medlemsvariabel. 87

OBS! Det går också att tilldela adressen för objektet cir av subklassen CIRKEL till en pekare fp av basklassen FIGUR. Objektet blir dock av typen FIGUR och FIGUR:s skrivfunktion körs. Man kan dock med hjälp av virtual-markering framför skrivfunktionen, ange att den ska bindas dynamiskt dvs bindas då pekaren vet vilket objekt den pekar på. class FIGUR char namn[20]; FIGUR(char *namn = ""); ~FIGUR(); virtual void skriv(); ; // OBS! virtual Utskriften blir nu istället : Figurnamn : Cirkel Radie : 1 Area : 3.14159 Figurnamn : Rektangel Sida1 : 2 Sida2 : 4.5 Area : 9 Figurnamn : Cirkel Figurnamn : Cirkel Radie : 1 // OBS! Den sista utskriften anropar subklassens CIRKEL skrivfunktion även om man utnyttjar en pekare till basklassen FIGUR. Genom att vi angett virtual framför skrivfunktionen kommer denna att bindas till aktuellt objekt dynamiskt alltså under körning. Detta kallas dynamisk bindning. 88

5.1 Dynamisk bindning Dynamisk bindning innebär att man under programkörningen överlåter åt programmet att leta upp rätt funktion för aktuellt subobjekt. Man definierar en pekare till en basklass och tilldelar denna pekare adressen till olika subobjekt. Genom dynamisk bindning kommer rätt funktion att anropas vid rätt tillfälle. Man kan exempelvis köra igenom en hel lista av subobjekt och varje gång anropas korrekt subobjekts medlemsfunktion. Ofta används basklassen enbart som en abstrakt klass dvs man skapar aldrig några verkliga objekt av basklassen utan enbart pekare med vars hjälp man pekar ut subobjekt. Som tillverkare av klasser kan man tvinga användare att använda basklassen enbart som en abstrakt klass genom att tilldela de virtuella funktioner, som man vill ska omdefinieras, värdet 0 (NULL) vid specifikationen. Man säger att funktionen nu är äkta virtuell. Definierar ej användaren om äkta virtuella funktioner i sin subklass blir även subklassen abstrakt och eftersom man ej kan skapa objekt av abstrakta klasser, kan man ej deklarera objekt av subklassen heller o.s.v. Ex : Gör om klassen FIGUR till en abstrakt basklass genom att göra areafunktionen äkta virtuell. // specifikation av klassen FIGUR class FIGUR char namn[20]; FIGUR(char *namn = ""); virtual ~FIGUR(); virtual void skriv(); virtual float area() = 0; ; // OBS! Klassen blir abstrakt // huvudprogram -- figurmai.cpp #include "cirkel.h" #include "rektang.h" void main() CIRKEL cir("cirkel", 1); REKTANGEL rek("rektangel", 2, 4.5); FIGUR fig; // Kompileringsfel! Kan ej skapa objekt // av abstrakta klasser. FIGUR *fp; fp = &cir; fp->skriv(); cout << "Area : " << fp->area(); fp = &rek; fp->skriv(); cout << "Area : " << fp->area(); 89

Utskriften blir nu : Figurnamn : Cirkel Radie : 1 Area : 3.14159 Figurnamn : Rektangel Sida1 : 2 Sida2 : 4.5 Area : 9 Samma resultat hade man åstadkommit med nedanstående program som allokerar subobjekten dynamiskt : #include "cirkel.h" #include "rektang.h" void main() FIGUR *fp; fp = new CIRKEL("Cirkel", 1); fp->skriv(); cout << "Area : " << fp->area(); delete fp; fp = new REKTANGEL("Rektangel", 2, 4.5); fp->skriv(); cout << "Area : " << fp->area(); delete fp; OBS! Destruktorn måste också definieras virtuell eftersom annars körs endast basklassens destruktor. Definierar man destruktorn som virtuell kommer subklassens destruktor att köras först och därefter basklassens. 90

Ex : I kylsystemet har man ett stort antal VVS-komponenter som man ska löpa igenom och göra beräkningar och regleringar på. Här är det lämpligt att definiera en basklass, VVS och göra en länkad lista av VVS-komponenter som man löper igenom varje cykel. Varje subklass har en dynamikfunktion som utför beräkningarna. Genom dynamisk bindning körs rätt dynamikfunktion vid rätt tillfälle. komplista Ventil1 Ventil2 Pump Filter Ventil3 Komplista, som blir av typen stack, skapas i konstruktorn för klassen KYLSYSTEM enligt: class KYLSYSTEM.... ; VVS *komplista; NOD *np1, *np2,....... KYLSYSTEM::KYLSYSTEM().... komplista = NULL; np1 = new NOD(komplista,....);.... I de olika händelsefunktionerna skaffar man sig en lokal iterator kp med vars hjälp man löper igenom listan med komponenter enligt exempelvis: void KYLSYSTEM::timer_Tick(Object *Sender, eventargs *e) VVS *kp = komplista; while (kp) kp->dynamik(); kp->display(); kp = kp->get_next(); 91

jälva hoplänkningen av komponenterna sker i konstruktorn för VVS-klassen enligt: class VVS VVS *next; // pekare till nästa komponent protected: char namn[10]; // namn på komponenten int x, y; // koordinater VVS( VVS *&next, char *namn, int x, int y); // konstruktor virtual void dynamik() // dummy dynamik virtual void display() // dummy display VVS *get_next(); // nästa komponent..... VVS::VVS(VVS *&next, char *namn, int x, int y) this->next = next; next = this; strcpy(this->namn, namn); this->x = x; this->y = y; VVS *VVS::get _next() return next; OBS! *& är en referens till en pekare. Detta måste man ha för att kunna ändra pekarvärdet next då nya komponenter stoppas in. OBS! Medlemsvariablerna namn, x och y är märkta protected vilket innebär att de kan användas direkt av en subklass som VENTIL (se nedan) men däremot ej av instansierade objekt.. OBS! Hur man utnyttjar this-pekaren för att länka ihop komponenterna och ge nextparametern ett nytt värde. Alla objekt har en this-pekare som pekar på sig själv. OBS! Här har man valt att ej definiera dynamikfunktionen äkta virtuell. Detta innebär att VVS-klassen ej blir abstrakt. Man kan alltså skapa objekt av klassen VVS och det kan finnas subklasser, som ej har någon överlagrad dynamikfunktion och som man också kan skapa objekt av. 92

VENTIL-klassen ärver all data från VVS-klassen och man lägger till de data och de funktioner som är speciella för ventiler. Man ska också förändra VENTIL-objektens funktionalitet gentemot VVS-objekten genom att definiera en ny överlagrad dynamikfunktion istället för den dynamikfunktion i VVS som inte gör någonting. Detsamma gäller display, rita, klick etc. class VENTIL : public VVS // OBS! Ärver från VVS float f; NOD *in; NOD *ut;... // flödet i ventilen // in-nod // ut-nod ; VENTIL(VVS *&next, char *namn, int x, int y, NOD *in, NOD *ut); void dynamik();... VENTIL::VENTIL(VVS *&next, char *namn, int x, int y, NOD *in, NOD *ut) : VVS(next, namn, x, y) this->in = in; this->ut = ut; OBS! Hur man överför parametrar till basklassens konstruktor genom att i subklassens initieringslista vissa parametrar slussas vidare till anropet av basklassens konstruktor. Basklassens konstruktor körs alltid innan subklassens. 93

En klass kan ses som en server som kan ha två olika typer av klienter: 1) Användarklient : En klient som via objekt eller pekare till objekt av klassen använder de publika medlemmarna. 2) Arvsklient : En klient som skapar nya subklasser genom att ärva data och funktioner från klassen. En tillverkare av en server-klass bestämmer själv hur medlemmarna ska skyddas mot klienterna genom att markera medlemmarna som private, protected eller public. För en användarklient innebär detta att enbart de publica medlemmarna kan användas emedan en arvsklient kan utnyttja både protected- och public-medlemmar. En arvsklient kan dessutom ange hur den ska ärva egenskaper public, protected eller private enligt : eller eller class SUBKLASS : public BASKLASS class SUBKLASS : protected BASKLASS class SUBKLASS : private BASKLASS Hur man som arvsklient ärver inverkar på fortsättningen av arvskedjan och användarkedjan. Ärver man public så får subklassen samma skydd mot nya klienter som basklassen. Ärver man protected ändras skyddet för de ärvda publica medlemmarna från basklassen till protected i subklassen och dessa kan alltså ej användas av användarklienter till subklassen utan bara av arvsklienter. Ärver man private blir alla ärvda medlemmar från basklassen private i subklassen och kan ej användas av några klienter alls utan det är bara subklassen själv som kan utnyttja de ärvda medlemmarna. På detta sätt kan man bryta en arvskedja och stoppa vidare arv. 94

5.2 Multipelt arv Arv kan man utnyttja i många steg. En kvadrat är en rektangel och en rektangel är en figur. Man kan också i C++ ha multipelt arv dvs man kan ärva från flera klasser. Här ska man egentligen fråga sig om ett subobjekt är ett basobjekt 1 och ett basobjekt 2. Ex: En student 'är både en' kårmedlem och en låntagare på biblioteket och ärver exempelvis medlemsnummer från kårmedlem och låntagarnummer från låntagare. Ofta använder man också multipelt arv då en klass ärver egenskaper från flera andra klasser utan att man egentligen kan säga att objekt av klassen är objekt av båda de klasser som man ärver ifrån. Ex: En avrundad rektangelklass, AVREK, som består av en rektangel med cirkelrundade kortsidor, ärver egenskaper från både CIRKEL och REKTANGEL enligt : FIGUR CIRKEL REKTANGEL AVREK 95

// specifikation av klassen AVREK -- avrek.h #include "rektang.h" #include "cirkel.h" class AVREK : public REKTANGEL, public CIRKEL // OBS! AVREK(char *namn, float sida, float radie); float area(); void skriv(); ; // implementation av klassen AVREK -- avrek.cpp AVREK::AVREK(char *namn, float sida, float radie) : REKTANGEL(namn, sida, 2*radie), CIRKEL(namn, radie) // OBS! float AVREK::area() return REKTANGEL::area() + CIRKEL::area(); void AVREK::skriv() REKTANGEL::skriv(); // huvudprogram -- avrekmai.cpp #include "avrek.h" void main() AVREK avr("avrek", 100, 25); avr.skriv(); cout << "Area :" << avr.area() << endl; Vid multipelt arv uppstår två problem : 1) Om det finns medlemmar med samma namn i de klasser man ärver från protesterar kompilatorn. Detta kan man lösa genom att använda globaloperatorn :: eller genom att definiera om medlemmen inuti den nya klassen. I ovanstående exempel definierar man om funktionen skriv i AVREKklassen och då har kompilatorn inga problem med att hitta rätt funktion. Hade man inte gjort någon ny skrivfunktion i AVREK skulle man ha fått kompileringsfel vid anropet avr.skriv(). 96

2) Data från bas-bas-klassen FIGUR ärvs dubbelt av AVREK. Detta innebär att objektet avr innehåller namnsträngen två gånger vilket är slöseri med minne. För att slippa detta kan man se till att klasserna REKTANGEL och CIRKEL ärver FIGUR-klassen virtual enligt : class REKTANGEL : virtual public FIGUR // OBS! virtual... ; class CIRKEL : virtual public FIGUR // OBS! virtual... ; ingen Nu har man bara ett namn i AVREK-klassen men frågan är vilken av klasserna REKTANGEL eller CIRKEL anropar FIGUR:s konstruktor? Svaret är att av dem anropar konstruktorn eftersom de ärver virtuellt. Man måste anropa FIGUR:s konstruktor explicit från AVREK enligt : // implementation av klassen AVREK -- avrek.cpp AVREK::AVREK(char *namn, float sida, float radie) : REKTANGEL(namn, sida, 2*radie), CIRKEL(namn, radie),, FIGUR(namn) // OBS!... Multipelt arv är ett omtvistat kapitel inom objektorientering. Många objektorienterade språk saknar denna möjlighet medan andra, som C++, har den implementerad. Man märker av ovanstående exempel att multipelt arv kan ställa till problem. Regeln som man bör ha är att använda multipelt arv i yttersta nödfall. Kan man undvara multipelt arv så bör man göra det. Ovanstående exempel kunde man ha löst med en aggregatrelation. AVREK består av ett REKTANGEL-objekt och ett CIRKEL-objekt. 97

Ex: Skriv om klassen AVREK ovan så att den istället för arv använder REKTANGELobjekt och CIRKEL-objekt som medlemsobjekt. // specifikation av klassen AVREK -- avrek.h #include "rektang.h" #include "cirkel.h" class AVREK REKTANGEL rek; CIRKEL cir; AVREK(char *namn, float sida, float radie); float area(); void skriv(); ; // implementation av klassen AVREK -- avrek.cpp AVREK::AVREK(char *namn, float sida, float radie) : rek(namn, sida, 2*radie), cir(namn, radie) // OBS! float AVREK::area() return rek.area() + cir.area(); void AVREK::skriv() rek.skriv(); // huvudprogram -- avrekmai.cpp #include "avrek.h" void main() AVREK avr("avrek", 100, 25); avr.skriv(); cout << "Area :" << avr.area() << endl; 98