812347A Olio-ohjelmointi, 2015 syksy 2. vsk III Olioperusteinen ohjelmointi C++:lla
Sisältö 1. Johdanto 2. Luokan määrittely ja jäsenten näkyvyys 3. Olion muodostaminen ja tuhoaminen 4. Saanti- ja asetusmetodit 5. Semantiikaltaan epätavalliset luokkien jäsenet 6. Koosteolioista ohjelmointi C++:lla 2
III.1 Johdanto Olioperusteinen ohjelmointi kapseloi luokkaan Jäsenmuuttujat eli attribuutit (datan, tilan) Metodit (datan käsittelyn, käyttäytymisen) Luokasta voidaan luoda useita ilmentymiä eli olioita Luokka määrittelee toimenpiteet, joita oliolle voi tehdä Luokkamääritys on täten myös tyyppimääritys ohjelmointi C++:lla 3
III.1 Johdanto (2) C++:ssa olio on samanlainen muuttuja kuin sisäisten tyyppien muuttuja Olio voi olla eliniältään 1. Automaattinen: kääntäjä huolehtii sen alustamisesta ja tuhoamisesta 2. Staattinen: määrittelyn jälkeen voimassa koko ohjelman suorituksen ajan 3. Dynaaminen: tuhoaminen on ohjelmoijan vastuulla Olio voidaan määritellä vakioksi lisäämällä oliomääritykseen avainsana const ohjelmointi C++:lla 4
III.2. Luokan määrittely ja jäsenten näkyvyys Yleensä erillisessä otsikkotiedostossa Luokkamääritys koostuu kolmesta osasta Avainsanasta class (myös struct tai union) Luokan nimestä Puolipisteeseen päättyvästä lohkosta: class LuokkaNimi { attribuuttien esittely metodien esittely }; // HUOMAA PUOLIPISTE! ohjelmointi C++:lla 5
III.2. Luokan määrittely ja jäsenten näkyvyys(2) Luokan sisällä esitellään luokan metodit (funktiot) ja määritellään attribuutit Lyhyet funktiot voi myös määritellä luokassa Luokkaan voidaan määritellä näkyvyydeltään kolmenlaisia jäseniä Julkisia (public) Suojattuja (protected) Yksityisiä (private) Luokka muodostaa samalla nimiavaruuden, joka kätkee jäsenten nimet Saman nimisiä jäseniä voidaan käyttää useassa eri luokassa ohjelmointi C++:lla 6
III.2.1 Julkiset jäsenet Julkiset jäsenet muodostavat luokan julkisen rajapinnan, jota kautta olioita voidaan käyttää Voidaan käyttää oliomuuttujan kautta piste- tai nuolinotaation avulla Kuten tietueen tietojäseniä Rajapinnan tulee olla tarpeeksi kattava Ei kuitenkaan liian monimutkainen Määritellään avainsanan public ja kaksoispisteen avulla määrittelylohko, joka on voimassa määrittelyn loppuun tai toisen näkyvyyslohkon määrittelyyn ohjelmointi C++:lla 7
III.2.2 Yksityiset jäsenet Voidaan käyttää vain luokan omissa jäsenfunktioissa Ei edes luokan aliluokissa, vaikka jäsenet periytyvätkin niille Suojatut (protected) jäsenet näkyvät aliluokissa, eivät luokkahierarkian ulkopuolella Määritellään avainsanan private ja kaksoispisteen avulla määrittelylohko, joka on voimassa määrittelyn loppuun tai toisen näkyvyyslohkon määrittelyyn Oletusnäkyvyys kun luokka on määritelty avainsanalla class ohjelmointi C++:lla 8
III.2.3 Jäsenmuuttujat ja jäsenfunktiot Jäsenmuuttuja on luokkaan määritelty muuttuja Kutsutaan myös attribuutiksi Attribuutit ovat kaikkien jäsenfunktioiden käytettävissä kuten funktioiden paikalliset muuttujat Hyvässä suunnittelutavassa luokissa ei yleensä määritellä julkisia attribuutteja Hyvin vähän suojattuja attribuutteja ohjelmointi C++:lla 9
III.2.3 Jäsenmuuttujat ja jäsenfunktiot (2) Jäsenfunktio on luokkaan määritelty funktio Sanotaan myös metodiksi Funktion koko nimi koostuu Luokan nimestä Näkyvyysoperaattorista (kahdesta kaksoispisteestä) Funktion nimestä Julkista jäsenfunktiota kutsutaan oliomuuttujan kautta, joko piste- tai nuolinotaation avulla ohjelmointi C++:lla 10
III.2.3 Jäsenmuuttujat ja jäsenfunktiot (3) this osoitin osoittaa käsiteltävään olioon Automaattisesti olemassa Sen tyyppi on Luokan_nimi* const this Välitetään näkymättömänä parametrina metodeille Käytetään enimmäkseen metodin paluuarvona Jos jäsenfunktiossa käytetään luokan attribuutin nimistä muuttujaa peittää jäsenfunktion paikallinen muuttuja attribuutin Tällöin attribuuttia käytetään this -osoittimen kautta ohjelmointi C++:lla 11
Esimerkki: Luokan määritys // Koodi: Ika.h #ifndef INCLUDED_IKA_H #define INCLUDED_IKA_H Luokkakaavio class Ika { public: long annaikapaivissa(); void asetaikapaivissa(long p); private: long paivia; }; #endif ohjelmointi C++:lla 12
Esimerkki: Metodien määritys // Ika.cpp #include Ika.h void Ika::asetaIkaPaivissa(long p) { paivia = p; } long Ika::annaIkaPaivissa() { return paivia; } ohjelmointi C++:lla 13
III.3 Olion muodostaminen ja tuhoaminen III.3.1 Muodostin eli konstruktori Luokan jäsenfunktio jolla ei ole paluuarvotyyppiä (ei edes void) jonka nimi on sama kuin luokan nimi Muodostimen avulla alustetaan olion attribuutit oliota luotaessa Kutsutaan automaattisesti olion luonnin yhteydessä Kutsun jälkeen olio on käyttökelpoinen ja ehjä Voi aiheuttaa poikkeuksen, jolloin oliota ei luoda ohjelmointi C++:lla 14
III.3.2 Oletusmuodostin Muodostin, jota voidaan kutsua ilman parametreja Muodostin, jolla ei ole parametreja tai kaikille parametreille on annettu oletusarvo Luokalla voi olla kerrallaan vain yksi oletusmuodostin Ellei ohjelmoija tee lainkaan muodostimia, kääntäjä generoi oletusmuodostimen Kääntäjän luoma oletusmuodostin ei tee mitään Tarvitaan yleensä luokassa ohjelmointi C++:lla 15
III.3.3 Alustuslista Muodostimen määrittelyyn voidaan liittää alustuslista Tällöin attribuuttien arvot asetetaan niiden määrittelyiden yhteydessä Sijoitetaan muodostimen parametriluettelon jälkeen ennen sen runkoa Alkaa kaksoispisteellä ja sisältää pilkulla erotettuna attribuutit, joiden alkuarvoksi tulee attribuutin yhteydessä olevien sulkeiden sisällä oleva arvo Alustuslistaa tarvitaan kun alustettava attribuutti on vakio, kun attribuutin tyyppi on viittaustyyppinen, yliluokkien alustamisen yhteydessä. ohjelmointi C++:lla 16
Esimerkki: Muodostimien esittely class Ika { public: Ika(); Ika(long paivia); Ika(const PaivaMaara&, const PaivaMaara&); void asetaikapaivissa(long paivia); long annaikapaivissa(); private: long paivia; }; ohjelmointi C++:lla 17
Esimerkki: Muodostimien määrittely ja alustuslista #include Ika.h Ika::Ika() { // Oletustoimenpiteet } Ika::Ika(long p) : paivia(p) // Alustuslista { } Ika::Ika(const PaivaMaara& nyt, const PaivaMaara& syntymapaiva){ // Laske päivien lukumäärä } ohjelmointi C++:lla 18
III.3.4 Kopiointimuodostin Kopiointimuodostimen avulla saman tyyppisestä oliosta alustetaan uusi olio Ilman eksplisiittistä kopiointimuodostinta kääntäjä tekee automaattisesti sellaisen tarvittaessa Kopioi jokaisen attribuutin yksi kerrallaan Jos kääntäjän tekemä kopiointimuodostin ei ole semanttisesti järkevä joudutaan laatimaan itse Tarvitaan varsinkin jos attribuutit ovat osoitintyyppisiä Muotoa Luokka::Luokka(const Luokka&); ohjelmointi C++:lla 19
III.3.5 Sijoitusoperaattori Kopiointimuodostimen pari Jos kopiointimuodostin tai sijoitusoperaattori on määritelty pitää yleensä toinenkin määritellä Muotoa Luokka& operator=(const Luokka&); ohjelmointi C++:lla 20
Esimerkki: Sijoitusoperaattori // Piste.h class Piste { private: double x_coord; double y_coord; public: Piste(double x,double y); Piste(const Piste& p); Piste& operator=(const Piste& p); }; ohjelmointi C++:lla 21
Esimerkki: Sijoitusoperaattori (2) #include Piste.h Piste::Piste(double x, double y):x_coord(x),y_coord(y){} Piste::Piste(const Piste& p){ x_coord = p.x_coord; y_coord = p.y_coord; } Piste& Piste::operator=(const Piste& p){ if(this == &p) return *this; x_coord = p.x_coord; y_coord = p.y_coord; return *this; } ohjelmointi C++:lla 22
III.3.6 Olion muodostamisen estäminen Jossakin tilanteessa olioiden suora muodostaminen ei ole mielekästä Olioiden muodostaminen voidaan estää määrittelemällä muodostimet yksityisiksi jäseniksi Muodostaminen suoraan aiheuttaa käännösaikaisen virheilmoituksen Kopioiden tekeminen voidaan estää esittelemällä yksityinen kopiomuodostin ja sijoitusoperaattori, mutta jättämällä määrittelemättä Tällöin kääntäjä ei tee oletustoteutusta niille ja käyttäminen aiheuttaa käännösaikaisen virheilmoituksen ohjelmointi C++:lla 23
III.3.7 Olion tuhoaminen: Hajotin Jäsenfunktio, jota kutsutaan automaattisesti kun olio tuhotaan Hajottimen nimi koostuu merkistä ~ ja luokan nimestä Ei tyyppiä, ei parametreja Luokassa voi olla vain yksi hajotin Hajottimen käyttötarkoitus: vapautetaan resurssit, jotka otettiin käyttöön oliota luotaessa Hajottimen jälkeen kutsutaan kunkin attribuutin hajotinta käänteisessä järjestyksessä Huom: Jos attribuutti on osoitintyyppinen sen hajotin tuhoaa vain osoittimen, ei sen osoittamaa muistia Hajotin EI SAA KOSKAAN aiheuttaa poikkeusta ohjelmointi C++:lla 24
III.3.8 Dynaamisesta muistinhallinnasta Tarkoittaa kekodynaamisen muistin käyttöä Varaaminen kekomuistista/vapauttaminen operaattoreilla new/delete new palauttaa osoittimen varattuun muistiin -> paluuarvo tietotyypiltään osoitin varattavan olion tai muuttujan tyyppiin new: kutsutaan automaattisesti tyypin muodostinta delete : kutsutaan automaattisesti tyypin hajotinta Olio* obj = new Olio(); //... sometime later: delete obj; ohjelmointi C++:lla 25
III.3.8.1 Dynaamiset taulukot Perustietotyypin taulukot: double *lukutaulu; // Osoitin taulukon 1. alkioon lukutaulu = new double[100]; // Taulukon varaaminen lukutaulu[10] = 1.0; // Taulukkoon sijoittaminen delete[] lukutaulu // Taulukon tuhoaminen Oliotaulukot: Oliot luodaan samalla kuin taulukkokin Luokalla oltava oletusmuodostin MyType * array = new MyType[100]; //... delete [] array; ohjelmointi C++:lla 26
III.4 Saanti- ja asetusmetodit Jäsenmuuttujat tavallisesti private-tyyppisiä, jotta olion tilaa ei voi vahingossa sotkea -> Jäsenmuuttujien arvoja ei voi suoraan lukea tai muuttaa HUOM! Public-tyyppisiä jäsenmuuttujia käytettävä vain erityisissä tilanteissa! Mikäli jäsenmuuttujan arvoa tarvitaan luokan ulkopuolella, kirjoitettava julkinen saantimetodi (getter), joka palauttaa muuttujan arvon Mikäli jäsenmuuttujan arvoa muutettava luokan ulkopuolelta, kirjoitettava julkinen asetusmetodi (setter), joka asettaa muuttujan arvon ja valvoo että sijoitus on laillinen ohjelmointi C++:lla 27
III.5 Semantiikaltaan epätavalliset luokkien jäsenet Luokkiin voi määritellä seuraavan laisia tietojäseniä, joiden semantiikka on erilainen kuin normaalien tietojäsenten: 1. Vakiojäsenet 2. Viittausattribuutit 3. Luokkajäsenet 4. Vakiomuotoiset luokka-attribuutit ohjelmointi C++:lla 28
III.5.1.1 Vakiomuotoiset attribuutit Luokan attribuutteja joita ei voi muuttaa määrittelyn jälkeen Alustettava muodostimen alustuslistassa Määritellään lisäämällä attribuutin tyypin eteen constavainsana ohjelmointi C++:lla 29
III.5.1.2 Vakiomuotoiset metodit Metodeja, jotka eivät voi muuttaa olion tilaa (attribuuttien arvoja) Voidaan kutsua olioille, jotka ovat vakio-olioita Vakiomuotoisuus kuuluu jäsenfunktion tyyppiin Voidaan siis ylikuormittaa funktio sekä normaaliksi että vakiomuotoiseksi Vakiomuotoinen jäsenfunktio esitellään ja määritellään lisäämällä parametriluettelon perään avainsana const Vakiometodin this-osoitin muotoa const TYYPPI* ohjelmointi C++:lla 30
III.5.2 Viittausmuotoiset attribuutit Alustettava aina muodostimessa Alustamisen tapahduttava alustuslistassa Alustuslistan ulkopuolella muutetaan aina viittausattribuutin kohteen arvoa Ei siis viittausattribuutin arvoa ohjelmointi C++:lla 31
III.5.3 Luokkajäsenet Kaikille luokan olioille yhteisiä jäseniä Määritellään C++:ssa avainsanalla static Luokkajäseniin voidaan viitata suoraan ilman oliota luokan nimellä Luokka::jäsen Näkyvyyssäännöt koskevat myös luokkajäseniä, esimerkiksi yksityisiä luokkajäseniä voidaan käyttää vain luokan metodeissa Luokkametodia kutsuttaessa ei tarvita luokasta oliota Luokkametodi voi viitata vain luokan toisiin luokkajäseniin Luokka-attribuutteihin Luokkametodeihin ohjelmointi C++:lla 32
III.6 Koosteolioista Olio voi koostua muista olioista Olioiden välillä koostumussuhde Muista olioista koostuva olio on koosteolio Koosteolioon sisältyvä olio on osaolio Jos luokka käyttää toista luokkaa hyväkseen, se käyttää hyväkseen luokan tarjoamia palveluja eli delegoi Oletusarvoisesti käyttää vain julkista rajapintaa Delegointi mallintaa HAS-A suhteen ohjelmointi C++:lla 33
III.6 Koosteolioista (2) Koostamisessa on mahdollista sisällyttää monta tietyn luokan oliota Periytymisessä vain yksi olio Koosteolio toteutetaan määrittelemällä osaolio attribuuttina muuttujatyyppisenä, viittaustyyppisenä tai osoitintyyppisenä ohjelmointi C++:lla 34
III.6.1 Olio muuttujatyyppisenä attribuuttina Koosteolion ja olioattribuutin elinikä yhtä pitkät Koosteolion määrittelyn tilanvarauksessa luodaan samaan tilaan myös olioattribuutti Kun koosteolion olemassaolo lakkaa, lakkaa myös olioattribuutin olemassaolo. class OsaLuokka { public: private: }; class KoosteLuokka{ public: private: OsaLuokka osa; }; ohjelmointi C++:lla 35
Olio muuttujatyyppisenä attribuuttina : Esimerkki Luokkakaavio ohjelmointi C++:lla 36
Olio muuttujatyyppisenä attribuuttina : Esimerkki (2) class Henkilo { private: std::string etunimi; std::string sukunimi; std::string sotu; public: Henkilo(std::string en, std::string sn, std::string stu); ~Henkilo(){}; }; class Kirja{ private: std::string nimi; Henkilo tekija; std::string isbn; public: void print(std::ostream &os) const; Kirja(std::string n, const Henkilo &h, std::string isb); ~Kirja(){}; }; ohjelmointi C++:lla 37
Olio muuttujatyyppisenä attribuuttina: Esimerkki (3) Kirja::Kirja(std::string n, const Henkilo &h,std::string isb): nimi(n),tekija(h),isbn(isb) {} void Kirja::print(std::ostream &os) const { os << tekija.getetunimi() << " " << tekija.getsukunimi() <<":"<< std::endl; os << nimi << std::endl; os << "ISBN: " << isbn; } ohjelmointi C++:lla 38
III.6.2 Olio dynaamisena attribuuttina Voidaan toteuttaa myös löyhä sidos tai luokkien välinen assosiaatio Samaan osaolioon voi olla viittaus tai osoitin monesta koosteoliosta Osaoliota ei saa tuhota ennen kuin kaikki viittaukset poistuneet Yleensä toteutetaan jokin dynaaminen ominaisuus Voidaan vaihtaa olion olemassaolon aikana ohjelmointi C++:lla 39
Olio dynaamisena attribuuttina: Esimerkki class Kurssi { private: std::string nimi; Henkilo* popettaja; // Dynaaminen attribuutti int pisteet; }; public: void print(std::ostream &os) const; Kurssi(std::string n,const Henkilo &ope, int credits); Kurssi(const Kurssi &k); Kurssi& operator=(const Kurssi& k); ~Kurssi(); ohjelmointi C++:lla 40
Olio dynaamisena attribuuttina: Esimerkki (2) Konstruktori ja destruktori Kurssi::Kurssi(std::string n, const Henkilo &ope, int credits): nimi(n),pisteet(credits){ popettaja = new Henkilo(ope); } Kurssi::~Kurssi(){ if (popettaja!= 0) delete popettaja; } ohjelmointi C++:lla 41
Olio dynaamisena attribuuttina: Esimerkki (3) Kopiomuodostin ja sijoitusoperaattori Kurssi::Kurssi( const Kurssi &k){ nimi = k.nimi; pisteet = k.pisteet; Kurssi& Kurssi:: operator=(const Kurssi& k){ if (this == &k) return *this; if(k.popettaja!= 0) popettaja = new Henkilo(*(k.pOpettaja)); else popettaja = 0; } } nimi = k.nimi; pisteet = k.pisteet; if(popettaja!= 0) delete popettaja; popettaja = new Henkilo(*(k.pOpettaja)); return *this; ohjelmointi C++:lla 42