1 / 31

Föreläsning 5

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

ferrol
Download Presentation

Föreläsning 5

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Föreläsning 5

  2. Problemet • Vi har sett att vi kan ersätta de metoder vi ärver från överklassen med egen funktionalitet (polymorfism) class Bil : public Fordon{ Bil(Person & owner) : Fordon(owner) {}// Den nya versionen av GetPosition skall // returnera fordonets position förskjutet // 3 enheter i bägge axlar Point GetPosition() { return Point(m_position).Offset(3,3); }};

  3. Problemet • Vad händer då en funktion som tar emot ”Fordon” får en ”Bil” som parameter? void fordonsinfo(Fordon & fordon){// vad händer om fordon är ett objekt av // klassen ”Bil”? // skriver vi ut positionen som ges av // ”Fordon::GetPosition()” eller // ”Bil::GetPosition()”? cout << fordon.GetPosition();};

  4. Problemet – svar • Metoden ”Fordon::GetPosition” används • Detta beror på att allt funktionen fordonsinfo vet om är att det är ett fordon som har skickats som parameter. • Problemet orsakas av statisk bindning • Default-beteende i C++ • Vid kompileringstillfället bestäms det att ”Fordon::GetPosition()” skall användas

  5. Problemet – önskat resultat • I de flesta fall vill man att objektets mest aktuella metod skall exekveras, inte en gammal metod från någon överklass • I vårat fall vill vi att ”Bil::GetPosition()” skall användas istället

  6. Problemet – lösning • Om man istället vid exekveringstillfället (run-time) bestämmer vilken funktion som skall användas kan vi få önskat resultat • Dynamisk bindning • Vi vill använda oss av metoduppslagning

  7. Problemet – lösning forts. • C++ erbjuder en lösning för dynamisk bindning av metoder • Nyckelordet ”virtual” framför en metod säger åt C++ att metoduppslagning skall användas för denna specifika metod • ”virtual” metoder ”ärvs”, dvs är en metod virtuell i överklassen så är den virtuell i underklasserna

  8. Exempel (virtuell metod) class Fordon{public: Fordon() : m_position(0,0) {}virtual Point GetPosition() { return m_position; }protected: Point m_position;}; class Bil : public Fordon{public: Bil() {} // Inget anrop av Fordon() behövsvirtual Point GetPosition() { return Point(m_position).Offset(3,3); }};

  9. Problemet – följder • Dynamisk bindning är inte lika effektivt som statisk bindning • stor anledning till att statisk bindning är default i C++ • Dagens processorer hanterar dynamisk bindning mycket effektivt • Inte längre lika stor nackdel

  10. Husdjur class Pet{public:virtual void mess_up() { // Släpp hår överallt }protected: Color m_color;};

  11. Kanin class Rabbit : public Pet{public: // vi ärver alla metoder från Petprotected: Carrot m_favoriteCarrot;};

  12. Hund class Dog : public Pet{public:void eat_cat();virtualvoid mess_up() {// Bit sönder saker // Kräks på nya mattan }protected: Bone m_favoriteBone;};

  13. Katt class Cat : public Pet{public: void scratch_sofa(); virtual void mess_up() {// släpa in möss // gör dessutom som alla andra Pet::mess_up(); }protected:};

  14. Pure virtual • Antag att man inte kan bestämma beteende för en virtuell funktion i en överklass • Exempelvis: Pet::mess_up() har ingen bestämd definition. • Metoden kan då deklareras som rent virtuell (pure virtual) • mess_up() = 0; • Tvingar alla underklasser att implementera metoden mess_up()

  15. Exempel (pure virtual) • Vi finner inget passande standardbeteende för när husdjur stökar till • mess_up() blir en pure virtual metod class Pet{public:virtual void mess_up() = 0;protected: Color m_color;};

  16. Exempel (pure virtual) forts. • Vilket även innebär att alla underklasser måste implementera mess_up() class Rabbit : public Pet{public:virtual void mess_up() { // Släpp hår överallt }protected: Carrot m_favoriteCarrot;};

  17. Abstrakt överklass • Klass som innehåller en/flera rent virtuella metoder • Klasser med rent virtuella metoder kan ej instansieras • Exempel: Husdjur, hur skulle detta objekt se ut? • Underklass måste komplettera klassen för att kunna instansieras • Referenser/pekare till objekt av överklassen kan ändå anropa de rent virtuella metoderna

  18. Rent abstrakt överklass • Alla medlemmar är rent virtuella (pure virtual) • Definierar ett gränssnitt som arvtagare måste implementera (för att kunna instansieras)

  19. Virtuella destruktorer • När man använder virtuella metoder i en klass bör man ta som vana att även deklarera destruktorn virtuell. • Om man misstänker att klassen skall användas polymorft bör man även skapa en virtuell destruktor • Att inte skapa en virtuell destruktor kan i vissa fall få förödande konsekvenser

  20. Lillhjärna class Cerebellum // Lillhjärnan{public: ~Cerebellum() { cout << ”Signing off”; }virtual void react() { fight(); eat(); sleep(); reproduce(); }void fight();void sleep();void eat();void reproduce();};

  21. Storhjärna class Cerebrum : public Cerebellum // Storhjärnan{public: Cerebrum() { m_mem = new Memory(); } ~Cerebrum() { delete m_mem; }virtual void react() {switch(think()) {case 0: eat(); break;case 1: sleep(); break;case 2: reproduce(); break; } }int think(); // return appropriate actionprotected: Memory *m_mem;};

  22. Destruktorhaveri void main(){ Cerebellum * lill = new Cerebellum(); Cerebrum * stor = new Cerebrum(); squash(lill); // anropar lill-destruktor squash(stor); // anropar lill-destruktor// missar alltså stor-destruktorn pga att // destruktorn inte är virtual } void squash(Cerebellum * brain){// statisk bindning (~Cerebellum)delete brain;}

  23. Lösning • Lösningen på problemet med felaktigt destruktoranrop är givetvis att göra destruktorn virtual

  24. Fordon Bil Båt Amfibiebil Virtuellt arv • När man ärver multipelt från två eller fler klasser med gemensamma överklasser uppstår datadubblering • För att undvika detta kan man ärva klasser virtuellt, vilket gör att alla klasser i arvshierarkin endast finns med i en uppsättning

  25. Exempel (virtuellt arv) class Fordon{public: Fordon(Person & owner); Point GetPosition() { return m_position; } Person & GetOwner() { return m_owner; }protected: Point m_position; Person & m_owner;};

  26. Exempel (virtuellt arv) class Bil : publicvirtual Fordon {public: Bil(int age) { m_age = age; }void Print() { cout << ”Bil, årsmodell ” << m_model; }protected:int m_model;}; class Bat : publicvirtual Fordon{public: Bat(int length) { m_length = length; }void Print() { cout << ”Båt, längd ” << m_length; }protected:int m_length;};

  27. Exempel (multipelt arv) • Klassen ärver multipelt precis som tidigare (får nu en uppsättning fordonsegenskaper) 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 Printvoid Print() { Bil::Print(); Bat::Print(); }};

  28. Sammanfattning – virtuellt arv • Virtuellt arv måste specificeras redan på den nivån i klasshierarkin där de ärver från samma klass. • Dubblering av data kan undvikas genom att ärva ”virtual” • Varje datamedlemsaccess sker virtuellt • Prestandaförsämring, värre än virtuella funktioner

  29. Interface-klasser • Klasser utan datamedlemmar • Mestadels rent virtuella metoder • Kan ha enklare virtuella metoder med implementation • Undviker problem vid multipelt arv eftersom ingen data kan dubbleras • Ärv maximalt en klass med implementation, resten Interface-klasser • Namnkollisionsproblematiken kvarstår dock

  30. Sammanfattning • Polymorf metod (polymorfism) • ”metod med många former” • Egenskapen att kunna ha flera olika beteenden beroende på objekt • Ersättning av metoder i underklasser • Statisk bindning • Kompilatorn bestämmer vid kompileringstillfället vilken metod som skall anropas

  31. Sammanfattning forts. • Dynamisk bindning • Programmet avgör vilken metod som skall anropas först när programmet kör (run-time) • Åstadkoms i C++ genom ”virtual” • Inte lika effektivt som statisk bindning, men inte så allvarligt på dagens processorer

More Related