1 / 90

Olio-ohjelmoinnin perusteet luento 4: Periytymisestä

Olio-ohjelmoinnin perusteet luento 4: Periytymisestä. Jani Rönkkönen jani.ronkkonen@lut.fi Luennot muokattu Sami Jantusen ja Kari Smolanderin aikaisempien vuosien luennoista. Sisältö. Johdanto Kertausta Esimerkki Yhteenveto Luokkien näkyvyysmääreet Periytyminen Toiminnan korvaaminen

milica
Télécharger la présentation

Olio-ohjelmoinnin perusteet luento 4: Periytymisestä

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. Olio-ohjelmoinnin perusteetluento 4: Periytymisestä Jani Rönkkönen jani.ronkkonen@lut.fi Luennot muokattu Sami Jantusen ja Kari Smolanderin aikaisempien vuosien luennoista

  2. Sisältö • Johdanto • Kertausta • Esimerkki • Yhteenveto • Luokkien näkyvyysmääreet • Periytyminen • Toiminnan korvaaminen • Moniperiytyminen • Yhteenveto

  3. PeriytyminenMuistatko vielä….? • Periytyminen tarkoittaa periaatetta siitä, että yleisempi määrittely on myös voimassa erikoistuneissa olioissa • Sanomme, että kukkakauppias perii myös kauppiaan ja ihmisen toiminnallisuuden Ihminen Kauppias Kukkakauppias

  4. PeriytyminenMuistatko vielä….? Perinnän idea: • Luokat voidaan organisoida hierarkkisiin periytymispuihin • Lapsiluokka perii vanhempiensa tiedon ja toiminnallisuuden • Abstrakti isäluokka on sellainen, josta ei voida tehdä omaa oliota, mutta jota käytetään lapsiluokkien määrittelyssä

  5. PeriytyminenMuistatko vielä….?

  6. Eläin ... Niveljalkainen Selkäjänteinen ... ... Hämähäkkieläin Hyönteinen Matelija Nisäkäs Lintu ... ... ... ... ... Leppäkerttu Kissa Ihminen PeriytyminenMuistatko vielä….?

  7. Luokkahierarkia Mammal int weight giveBirth( ) Kuinka monta attribuuttia koiralla on? Land-Mammal int numLegs Dog boolean rabid ... Chihuahua SheepDog

  8. Huomaathan että: • Land-Mammal on Dog- luokan isäluokka (superclass), mutta Mammal –luokan lapsiluokka (subclass) • Dog –luokalla on kolme attribuuttia • weight, numLegs ja rabid • kaksi attribuuttia perinnän kautta ja yksi omassa luokassa

  9. Muistatko vielä? Koirat eläintarhassa CDog.h #ifndef CDog_H #define CDog_H #include <iostream.h> class CDog { int weight; bool rabidOrNot; std:string name; public: CDog (int x, std::string y); ~CDog(); //tuhoajan esittely bool getRabid ( )const; void setRabid (bool x); std::string getName ( )const; void setName (std::string z); int getWeight ( )const; void setWeight (int x); void eat( ); void growl( )const; }; #endif /* CDog_H */ • Vielä paljon kerrottavaa Ei vielä ihan tyylipuhdas luokan esittely. CDog boolrabidOrNot intweight stringname void growl() void eat()

  10. Muistatko vielä? Koirat eläintarhassa CDog.cpp bool CDog::getRabid ( ) const{ return rabidOrNot; } void CDog::setRabid (bool x) { rabidOrNot = x; } int CDog::getWeight ( ) const{ return weight; } void CDog::setWeight (int y) { weight = y; } string CDog::getName ( ) const{ return name; } void setName (string z) { name = z; } #include <string.h> #include “CDog.h” using namespace std; // Constructor CDog::CDog (int x, string y) { rabidOrNot = false; weight = x; name = y; } // destructor CDog::~CDog() {} void CDog::eat ( ) { cout << name << “ is eating” << endl; weight++; } void CDog::growl ( ) const{ cout << “Grrrr”; }

  11. Ongelmia! • Mikä tahansa koira ei kelpaa! Puudeli se olla pitää! Miten saamme luotua puudelin siten, ettei tarvitsisi kirjoittaa paljon koodia uusiksi??

  12. periytyminen:Puudeli on koira • Puudeli on koira (muistathan “is-a” testin) • Käytetään hyväksi CDog-luokan toteutus perimällä siitä CPoodle-luokka CDog boolrabidOrNot intweight stringname void growl() void eat() CPoodle

  13. No niin…. Ryhdytään hommiin!Luodaan puudeli-luokka (sekä .h, että .cpp tiedostot) Tässä suoritetaan periytyminen CDog -luokasta #include "CDog.h“ CPoodle.h class CPoodle: public CDog { public: CPoodle(int x, std::string y); }; #include <iostream> #include "CPoodle.h" using namespace std; CPoodle::CPoodle(int x, string y) : CDog (x,y){ cout << “Tuli muuten tehtyä puudeli" << endl;} CPoodle.cpp

  14. Mitäs täällä tapahtuu? Isäluokan rakentajaa kutsutaan aina!* CPoodle.cpp CPoodle::CPoodle(int x, string y) : CDog (x,y) { cout << “Tuli muuten tehtyä puudeli" << endl; } Normaalia rakentaja tavaraa *Huomaa!: Jos isäluokan rakentajaa ei kutsuta eksplisiittisesti itse, kääntäjä yrittää kutsua automaattisesti isäluokan oletusrakentajaa (mitä ei tässä esimerkissä ole olemassa)

  15. Mitä tuli taas tehtyä?? • Loimme puudeliluokan jolla on kaikki attribuutit ja metodit kun CDog-luokallakin

  16. Ongelmia! void CDog::growl ( ) { cout << “Grrrr”; } • Puudelit ei sano “Grrrrrrr”! Eihän??? • Ne sanoo “Yip”!

  17. Eläin Selkäjänteinen Muistatko vielä?Toiminnallisuuden korvaaminen • Nisäkkäät synnyttävät eläviä poikasia • Linnut munivat munia • The Australian Platypus on nisäkäs, mutta luo munia Nisäkäs Lintu

  18. Muistatko vielä?Toiminnallisuuden korvaaminen • On mahdollista korvata (override) isäluokassa määritelty toiminnallisuus toteuttamalla lapsiluokkaan saman niminen toiminnallisuus • Sopivan metodin etsintä aloitetaan aina lapsiluokasta. Jos lapsiluokassa ei ole toteutettuna haluttua toiminnallisuutta, siirrytään etsimään sitä isäluokasta

  19. Tehdäänpä jotain puudelin murinalle!!! GRRRRR • Korvataan CDog –luokan growl -metodi • Yksinkertaista, kirjoitetaan Puudeliluokkaan vain samanniminen metodi

  20. #include "CDog.h“ CPoodle.h class CPoodle:public CDog { public: CPoodle(int x, std::string y); void growl() const; }; Kiltti puudeli! YIP CPoodle.cpp CPoodle::CPoodle(int x, string y) : CDog (x,y) { cout << “Tuli muuten tehtyä puudeli" << endl; } void CPoodle::growl( ) const{ cout << "Yip!" << endl; }

  21. Mitä juuri opimme?Periytymisen määrittely Tässä suoritetaan periytyminen CDog -luokasta #include "CDog.h“ CPoodle.h class CPoodle:public CDog { public: CPoodle(int x, std::string y); }; Puhumme periytymisestä hetken kuluttua lisää!

  22. Mitä juuri opimme?Muodostimien käyttö periytymisen yhteydessä Isäluokan muodostinta kutsutaan aina!* CPoodle.cpp CPoodle::CPoodle(int x, string y) :CDog(x,y) { cout << “Tuli muuten tehtyä puudeli" << endl; } Normaalia rakentaja tavaraa Luokkia perittäessä on muodostinten ja purkajien käytössä on paljon huomioitavaa Puhumme tästä lisää ensi viikolla!

  23. Mitä juuri opimme?Toiminnan korvaaminen GRRRR YIP Toiminnan korvaaminen on oleellinen osa periytymistä. Puhumme tästä hetken kuluttua lisää!

  24. Missä mennään • Johdanto • Kertausta • Esimerkki • Yhteenveto • Luokkien näkyvyysmääreet • periytyminen • Toiminnan korvaaminen • Moniperiytyminen • Yhteenveto

  25. Luokan näkyvyysmääreet • class CPoodle • { • public: • //Tänne tulevat asiat näkyvät • //luokasta ulos • protected: • //Tänne tulevat asiat näkyvät • //vain aliluokille • private: • //Tänne tulevat asiat ei näy • // ulospäin • };

  26. Sääntöjä • Oletusarvoinen näkyvyysmääre on private • Saatat nähdä koodia, missä jäsenmuuttujat on määritelty luokassa ensimmäisenä ilman näkyvyysmäärettä (=private:). Huonoa tyyliä! • Selkeämpää kirjoittaa luokka public:, protected:, private: -järjestyksessä

  27. Public: • Julkinen rajapinta. Kaikki voivat käyttää • Luokka: Koira • murise • syö • kerroPaino • kerroNimi • oletkoVesikauhuinen

  28. Public: Ohjeita Ole huolellinen julkisen rajapinnan suunnittelussa! • Rajapinta on lupaus luokan tarjoamista palveluista. • Rajapintaan kuuluvien asioiden tulisi säilyä muuttumattomina • Rajapinnan tulisi olla “minimaalinen, mutta täydellinen” • Ei ylimääräistä tavaraa “varmuuden vuoksi” • Jos jotain puuttuu, niin luokan käyttäjällä ei mitään mahdollisuuksia korjata puutetta Minun luokka tekee tätä eikä mitään muuta

  29. Public: Ohjeita Jäsenmuuttujat on syytä pitää visusti piilossa! • Tiedon piilottaminen on yksi olioajattelun perusajatuksista • Voi tulla tarve siirtää jäsenmuuttuja muualle tai korvata se jollain toisella rakenteella • Et voi tehdä tätä jos olet jo julkaissut muuttujasi • On parempi, että oliolla on täysi kontrolli tietoonsa • Jos muuttuja on julkinen, olio ei voi mitenkään tietää milloin arvoa luetaan tai sitä muutetaan Et pääse “kopeloimaan” tietojani!

  30. Protected: • Käytetään perittäessä luokkia • Muulloin toimii samoin kuin private: • Sallii lapsiluokkien käyttää yläluokan jäseniä • Lapsiluokka ei pääse suoraan käsiksi yläluokalta perimiinsä private jäseniin

  31. Private: • Kaikkein rajoittavin näkyvyysmääre • Vain luokka itse voi käyttää private jäseniä (myös ystäväfunktiot ja –luokat, mutta tästä lisää myöhemmin) • samantyyppinen olio pääsee myös käsiksi toisen olion privaatteihin tietoihin • Julista privaatiksi: • jäsenmuuttujat • apufunktiot

  32. Missä mennään • Johdanto • Kertausta • Esimerkki • Yhteenveto • Luokkien näkyvyysmääreet • Periytyminen • Toiminnan korvaaminen • Moniperiytyminen • Yhteenveto

  33. Periytymisen määrittely Tässä suoritetaan periytyminen CDog -luokasta #include "CDog.h“ CPoodle.h class CPoodle: public CDog { public: CPoodle(int x, string y); };

  34. Periytymisen määrittelyclass CPoodle: public CDog • C++:ssa on oletuksena yksityinen periytyminen • class B : A {…} tarkoittaa samaa kuin class B : private A {…} • Kaikki isäluokasta perittävät asiat saavat private näkyvyysmääreen lapsiluokassa, riippumatta alkuperäisestä määrityksestä (eli poistuvat lapsiluokan julkisesta rajapinnasta) • Tämä on hieman outoa, sillä julkinen (public:) on kuitenkin yleisintä

  35. Periytymisen tavoistaEsimerkki (Hannu Peltosen kirjasta Johdatus C++ ohjelmointikieleen) • Haluamme rakentaa yleiskäyttöisen luokan, jota apuna käyttäen voimme toteuttaa erilaisia 2-suuntaisia listoja next prev data next prev data next prev data

  36. 2-suuntainen lista (Deque) • Ensimmäiseksi määrittelemme Deque luokan jonka alkioihin voi tallentaa mitä tahansa tietoa • Luokkaa ei käytetä suoraan, vaan siitä johdetaan uusia luokkia erityyppisten alkioiden tallentamista varten. next prev data next prev data next prev data

  37. 2-suuntainen lista (Deque)Määritellään ensin jäsenmuuttujat class Deque { … private: struct Item { Item *prev; Item *next; void *data; }; Item *first; Item *last; Item *curr; }; next prev data next prev data next prev data

  38. next prev data next prev data next prev data 2-suuntainen lista (Deque)Määritellään julkinen rajapinta void Deque::goBeforeFirst() { curr = first; } void Deque::goAfterLast() { curr = last; } void Deque::forth() { if (curr != last) curr = curr->next; } void Deque::back() { if (curr != first) curr = curr->prev; } int Deque::isBeforeFirst() const { return curr == first; } int Deque::isAfterLast() const { return curr == last; } class Deque { public: void goBeforeFirst(); void goAfterLast(); void forth(); void back(); int isBeforeFirst() const; int isAfterLast() const; ... };

  39. 2-suuntainen lista (Deque)Rakentaja/muodostin • Deque oli tarkoitettu yleiskäyttöiseksi luokaksi, joka ei voi esiintyä yksinään. Miten voidaan varmistua siitä, että ohjelmassa ei pysty määrittelemään suoraan tyyppiä Deque olevia muuttujia? next prev data next prev data next prev data

  40. 2-suuntainen lista (Deque)Rakentaja Deque::Deque() { first = new Item; last = new Item; first->prev = NULL; first->next = last; First->data = NULL; last->prev = first; last->next = NULL; Last->data = NULL; curr = first; }; class Deque { ... protected: Deque(); ... }; Kun rakentaja on protected, ei sitä voi kutsua muualta kuin periytetystä luokasta next prev data next prev data next prev data

  41. 2-suuntainen lista (Deque) Valmista? Puuttuuko vielä jotain?

  42. next prev data next prev data next prev data 2-suuntainen lista (Deque)Alkion lisäys void Deque::insertBeforeCurrent(void *p) { if (curr != first) { Item *newItem = new Item; newItem->data = p; newItem->next = curr; newItem->prev = curr->prev; curr->prev->next = newItem; curr->prev = newItem; curr = newItem; } } void Deque::insertAfterCurrent(void *p) { if (curr != last) { forth(); insertBeforeCurrent (p); } } class Deque { ... protected: void insertBeforeCurrent(void*); void insertAfterCurrent(void*); ... };

  43. next prev data next prev data next prev data 2-suuntainen lista (Deque)Alkion poisto void * Deque::removeCurrentAndGoBack() { return remove(curr->prev); } void * Deque::removeCurrentAndGoForth() { return remove(curr->next); } void * Deque::remove (Item *newCurr) { if (curr == first || curr == last ) return NULL; else { void *res = curr->data; curr->prev->next = curr->next; curr->next->prev = curr->prev; delete curr; curr = newCurr; return res; //palautetaan data } } class Deque { ... protected: void *removeCurrentAndGoBack(); void *removeCurrentAndGoForth(); private: void *remove(Item *newCurr); ... };

  44. 2-suuntainen lista (Deque)Nykyisen alkion saanti ja listan tuhoaminen void * Deque::current() const { return (curr == first || curr == last) ? NULL : curr->data; } Deque:: ~Deque () { Item *p, *next; for (p = first; p != NULL; p = next) { next = p->next; delete p->data; delete p; } } class Deque { ... protected: void *current () const; ~Deque(); ... }; next prev data next prev data next prev data

  45. Hurraa!!! VIHDOINKIN VALMISTA!

  46. Mitä tuli tehtyä? Loimme luokan joka on elegantti ja yleiskäyttöinen • Ei käytetä yksin vaan tästä johdetaan helposti erilaisia listaratkaisuja minimaallisella työllä • Koodin uudelleenkäyttö! • Hyvä esimerkki oliopohjaisuudesta ja perinnästä!

  47. Listoja liukuhihnalta!(IntDeque) Otetaanpa Deque luokka hyötykäyttöön! • Luodaan Int-pohjainen lista • IntDeque • void goBeforeFirst(); • void goAfterLast(); • void forth(); • void back(); • int isBeforeFirst() const; • int isAfterLast() const; • IntDeque(); • void insert (int); • void remove(); • int current () const; • ~IntDeque(); class IntDeque: public Deque { public: IntDeque(); void insert (int); void remove(); int current () const; ~IntDeque(); }; Deque-luokasta peritty Uusi toiminnallisuus next prev data next prev data next prev data

  48. IntDeque toteutus • int IntDeque::current() const • { • int *ptr = (int*)Deque::current(); • return (ptr != NULL )? *ptr : -1; • } • IntDeque:: ~IntDeque() • { • goBeforeFirst(); • forth(); • while (!isAfterLast()) • { • delete (int*)Deque::current(); • forth(); • } • } IntDeque::IntDeque() { //kutsutaan isäluokan }//oletusmuodostinta automaattisesti void IntDeque::insert (int n) { int *ptr = newint; *ptr = n; insertAfterCurrent (ptr); } void IntDeque::remove () { //Kutsutaan delete myös data:lle delete (int*) removeCurrentAndGoBack(); }

  49. IntDequeMitä opimme? • Koodin uudelleenkäyttö on helppoa ja mukavaa! • Kun perit public: -määreellä perit isäluokan rajapinnan ja saat sen public- ja protected –jäsenet käyttöösi

  50. Listoja liukuhihnaltaIntStack • Seuraavaksi haluamme tehdä pinon • Jotain pielessä! Mitä? • IntStack • void goBeforeFirst(); • void goAfterLast(); • void forth(); • void back(); • int isBeforeFirst() const; • int isAfterLast() const; • IntStack(); • int empty () const; • void push (int); • int top () const; • int pop (); • ~IntStack(); Deque-luokasta peritty Uusi toiminnallisuus

More Related