1 / 109

Olio-ohjelmoinnin perusteet luento 6: C++ tekniikoita, virhetilanteet ja poikkeukset

Olio-ohjelmoinnin perusteet luento 6: C++ tekniikoita, virhetilanteet ja poikkeukset. Sami Jantunen LTY/Tietotekniikan osasto. Sisältö. C++ tekniikoita Nimiavaruus Luokkamuuttujat Luokkafunktiot Operaattorien uudelleenmäärittely Ystäväfunktiot Ystäväluokat Virhetilanteet ja poikkeukset

Download Presentation

Olio-ohjelmoinnin perusteet luento 6: C++ tekniikoita, virhetilanteet ja poikkeukset

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 6: C++ tekniikoita, virhetilanteet ja poikkeukset Sami Jantunen LTY/Tietotekniikan osasto

  2. Sisältö • C++ tekniikoita • Nimiavaruus • Luokkamuuttujat • Luokkafunktiot • Operaattorien uudelleenmäärittely • Ystäväfunktiot • Ystäväluokat • Virhetilanteet ja poikkeukset • Syitä poikkeuksiin • Virhetilanteisiin reagointi • Virhehierarkiat • Poikkeuksien heitto ja sieppaaminen • Poikkeukset ja oliot • Yhteenveto

  3. Ongelma // vendor1.h ... various stuff ... class String { ... }; myProgram.cpp #include "vendor1.h" #include "vendor2.h" void main() { … } // vendor2.h ... various stuff ... class String { ... }; Löydätkö esimerkistä ongelman?

  4. Ongelman kuvaus • Käyttökelpoisia luokkien, funktioiden ja muuttujien nimiä on rajallinen määrä • Isommissa ohjelmistoissa törmätään helposti nimikonflikteihin. • Nimikonflikteja syntyy etenkin silloin, kun käytetään hyväksi monta eri ohjelmistomoduulia: • Esimerkkejä: • Ohjelmistossa käytössä olevista kirjastoista 2 määrittelee Tulosta() -funktion. • Käyttämässäsi 3. osapuolen luokkakirjastossa on määritelty samanniminen luokka kuin omassa koodissasi. ohjelma ei käänny

  5. Nimiavaruudet (Namespaces) • C++ ratkaiseen suurten ohjelmistojen rajapintojen nimikonfliktit nimiavaaruutta käyttämällä • Nimiavaruuksien tarkoituksena on tarjota kielen syntaksin tasolla oleva hierarkkinen nimeämiskäytäntö. • Hierarkia auttaa jakamaan ohjelmistoa osiin • Samalla estetään nimikonfliktit eri ohjelmiston osien välillä

  6. Nimiavaruuden määrittely • Toisiinsa liittyvät ohjelmakokonaisuudet voidaan koota yhteen nimiavaruuteen namespace –avainsanalla • Käyttöesimerkki. Kootaan kaikki päiväykseen liittyvät tiedot (tietorakenteet, tietotyypit, vakiot, oliot ja funktiot) yhteen nimikkeen Paivays alle: • paivays.cpp • #include “paivays.h” • namespace Paivays { • Pvm luo (int paiva, int kuukausi, int vuosi) • { • Pvm paluuarvo; • ... • return paluuarvo; • } • void tulosta (Pvm kohde) • { • ... • } • paivays.h • namespace Paivays { • struct Pvm { int p_, k_, v_; }; • Pvm luo (int paiva, int kuukausi, int vuosi); • void tulosta (Pvm kohde); • ... • }

  7. Näkyvyystarkenninoperaattori • Nimiavaruuden sisällä määritetyt jäsenet ovat näkyvissä vain kyseisen nimiavaruuden sisällä • Nimiavaruuden ulkopuolelta em. jäseniin pääsee käsiksi näkyvyystarkenninoperaattorin :: avulla. • Ohjelmoijan tulee jäseniä käytettäessä ilmaista mitä kokonaisuutta ja mite alkiota sen sisällä hän haluaa käyttää • Paivays::tulosta() • Kirja::tulosta() • Ylimääräistä kirjoittelua, mutta toisaalta selkiyttää koodia

  8. NimiavaruudetHyödyt • Nimikonfliktien vaara vähenee merkittävästi • jokaisen moduulin rajapintanimet ovat omassa nimetyssä näkyvyysalueessaan • Moduulin määrittelemien rakenteiden käyttö on kielen syntaksin tasolla näkyvän rakenteen (näkyvyystarkennin::) vuoksi selkeämpää • Hierarkisuudesta huolimatta moduulin sisällä on käytettävissä lyhyet nimet. • Koodista nähdään syntaksin tasolla esimerkiksi, mitkä funktiokutsut kohdistuvat saman moduulin sisälle ja mitkä muualle ohjelmistoon

  9. Korjataan ongelma • Enää ei ole käytössä kahta String-luokka • String-luokkien sijasta meillä on käytössä luokat: Vendor1::String Vendor2::String // vendor1.h ... various stuff ... namespace Vendor1 { class String { ... }; } // vendor2.h ... various stuff ... namespace Vendor2 { class String { ... }; }

  10. std nimiavaruus • Kaikki C++ standardi määrittelee omaan käyttöönsä std-nimiavaruuden • Käytössä kaikissa nykyaikaisissa kääntäjissä • std-nimiavaruus sisältää lähestulkoon kaikki C++ ja C-kielissä määritellyt rakenteet. Esim: • std::prinf • std::cout • Varattu pelkästään sisäiseen käyttöön. • Et saa lisätä omia rakenteita std-nimiavaruuteen

  11. Uudet otsikkotiedostot • std nimiavaruuden käyttöönotto aiheuttaa muutoksia myös otsikkotiedostojen nimissä • Kääntäjän omat otsikkotiedostot sisällytetään ilman .h –ekstensiota • #include <iostream.h>  #include<iostream> • C-kielestä perittyihin otsikkotiedostoiden nimiin lisätää ‘c’ eteen • #include <cstring>

  12. Esimerkki std-nimiavaruuden käytöstä • paivays.cpp • #include <cstdlib>//pääohjelman paluuarvo EXIT_SUCCESS • #include <iostream> //C++ tulostus • #include <cstring> //C:n merkkitaulukkofunktiot • int main() • { • const char* const p = “Jyrki Jokinen”; • char puskuri [42]; • std::strcpy(puskuri, “Jyke “); • std::strcat(puskuri, std::strstr(p, “Jokinen”) ); • std::cout << puskuri << std::endl; • return EXIT_SUCCESS; • }

  13. std::cout, std::endl joko väsyttää? • std:: toistaminen jatkuvasti turhauttaa • Jos samassa ohjelmalohkossa käytetään useita kertoja samaa nimiavaruuden sisällä olevaa nimeä, kannattaa käytttää using -lausetta • using mahdollistaa valittujen rakenteiden käytön nimiavaruuden ulkopuolella ilman :: -tarkenninta • normaali using -lause nostaa näkyville yhden nimen • using namespace nostaa näkyville kaikki nimiavaruuteen kuuluvat rakenteet

  14. usingEsimerkki • paivays.cpp • #include “paivays.h” • void kerroPaivays (Paivays::Pvm p ) • { • using std::cout; • using std::endl; • using namespace Paivays; //Käytetään kaikkia nimiavaruuden nimiä • cout << “Tänään on: “; • tulosta(p); //Kutsuu Paivays::Tulosta • cout << endl; • }

  15. usingOhjeita • Käytä using-lausetta mielellään vasta kaikkien #include-käskyjän jälkeen • Vältä using-lauseen käyttöä otsikkotiedostossa • Käytä using-lausetta mahdollisimman lähellä sitä aluetta, jossa sen on tarkoitus olla voimassa • Paljon käytetyissä rakenteissa on usein kuitenkin selkeämpää kirjoittaa using heti siihen liittyvän otsikkotiedoston #include-käskyn jälkeen.

  16. ehdotus using-lauseen käyttöstä eri otsikkotiedostojen kanssa • //Omat rakenteet esitellään std-kirjastoja ennen • //(tämä siksi että saamme tarkastettua niiden sisältävän kaikki • //tarvittavat #include-käskyt ts. ne ovat itsenäisesti kääntyviä yksikköjä • #include “paivays.h” • #include “swbus.h” • #include “tietokanta.h” • #include “loki.h” • //Kaikista yleisimmin tässä tiedostossa käytetyt std-rakenteet esitellään • //heti niihin liittyvän otsikkotiedoston jälkeen • #include <iostream> • using std::cout; • using std::endl; • #include <vector> • using std::vector; • #include <string> • using std::string • //lopuksi omiin moduuleihin liittyvät using-lauseet • using Paivays::PVM; • using Loki::varoitus; • using Loki::virhe;

  17. Nimiavaruuden synonyymi • Nimiavaruudelle voidaan määritellä synonyymi (alias) • alias-nimeen tehdyt viittaukset käyttäytyvät alkuperäisen nimen tavoin • Käyttökohteet • moduulin korvattavuus helpottuu • helpompi nimi pitkille nimiavaruuksien nimille

  18. Nimiavaruuden synonyymiEsimerkki • #include “prjlib/string.h” • #include <string> • int main() • { • #ifdef PRJLIB_OPTIMOINNIT_KAYTOSSA • namespace Str = ComAcmeFastPrjlib; • #else • namespace Str = std; • #endif • Str::string esimerkkijono; • . • . • . • }

  19. Missä mennään? • C++ tekniikoita • Nimiavaruus • Luokkamuuttujat • Luokkafunktiot • Operaattorien uudelleenmäärittely • Ystäväfunktiot • Ystäväluokat • Virhetilanteet ja poikkeukset • Syitä poikkeuksiin • Virhetilanteisiin reagointi • Virhehierarkiat • Poikkeuksien heitto ja sieppaaminen • Poikkeukset ja oliot • Yhteenveto

  20. Ongelma • Haluamme pitää kirjaa siitä kuinka monta samaa tyyppiä olevaa oliota on hengissä kullakin hetkellä • Mikä olisi hyvä ratkaisu? • Milloin tiedetään, että olio syntyy tai kuolee? • Missä pidetään kirjaa hengissä olevien olioiden lukumäärästä?

  21. Aloitetaan ongelman ratkaisu • Meillä pitää olla laskuri! • laskuria lisätään kun olion rakentajaa kutsutaan • laskuria vähennetään kun olion purkajaa kutsutaan • Millä näkyvyysalueella laskuri sijaitsee? • Luokan jäsenmuuttujana? • Globaali muuttuja?

  22. Hmmm… • Laskurin sijoittaminen olion jäsenmuuttujaksi ei toimi • se olisi oliokohtainen muuttuja. Muut eivät pääsisi päivittämään sitä • Globaali muuttuja toimisi • Olio-ohjelmoijina emme tykkäisi ideasta. Tämä olisi sotkuinen ratkaisu

  23. Hmmm… • Huomaamme, että joskus olisi tarvetta sellaisille jäsenille, jotka on yhteisiä kaikille luokan olioille! • toisaalta kaikille olioille • toisaalta ei millekkään niistä

  24. Ratkaisu! • C++ kielessä on mekanismi, mikä ratkaisee ongelmamme • On mahdollista esitellä luokan jäsen luokkamuuttujana (static data member) • Luokkamuuttujan esittely on muuten samanlainen kuin jäsenmuuttujankin, mutta esittely alkaa avainsanalla static

  25. Luokkamuuttuja • Luokkamuuttuja on luonteeltaan hyvin lähellä normaalia globaalia muuttujaa • Se on olemassa, vaikka luokasta ei olisi vielä luotu ainuttakaan oliota. • Koska luokkamuuttuja ei kuulu mihinkään olioista, sitä ei voi alustaa luokan rakentajassa. • Jossain päin koodia täytyy olla erikseen luokkamuuttujan määrittely, jonka yhteydessä muuttuja alustetaan: int X::luokkamuuttuja_ = 2;

  26. Luokkamuuttujan käyttö • Luokan omassa koodissa luokkamuuttujaan voi viitata aivan kuten jäsenmuuttujaankin • Luokan ulkopuolelta luokkamuuttujaan voi viitata syntaksilla: Luokka::lmuuttuja • Toinen tapa on viitata luokkamuuttujiin luokan olion kautta: X xolio; int arvo = xolio.luokkamuuttuja_; • Käytetään pitkälti saman tyylisesti kuin jäsenmuuttujiakin • pyri pitämään luokkamuuttujat privaatteina

  27. Luokkafunktiot (static member functions) • Edustavat sellaisia luokan palveluja ja operaatioita, jotka eivät kohdistu mihinkään yksittäiseen olioon • Samankaltainen jäsenfunktion määrittelyn kanssa • Lisätään static –avainsana • Ei saa käyttää toteutuksessa minkään olion jäsenmuuttujia eikä this-osoittimia

  28. Ja ratkaistaan ongelma! // static members in classes #include <iostream.h> class CDummy { public: static int n; CDummy () { n++; }; ~CDummy () { n--; }; }; int CDummy::n=0; int main () { CDummy a; CDummy b[5]; CDummy * c = new CDummy; cout << a.n << endl; delete c; cout << CDummy::n << endl; return 0; } • Mikä on oheisen koodin lopputulos? Vastaus: 7 6

  29. Hieman konkreettisempi esimerkki! • On hyvin tavallista että tietystä luokasta pitäisi olla olemassa vain ja ainoastaan yksi instanssi • Kyseinen instanssi pitäisi olla kuitenkin mahdollisimman helposti saatavilla muille olioille • Ongelma: • Kuinka varmistat, että luokkaa ei missään tilanteessa luoda enemää kuin yksi olio? • Kuinka voit samalla taata sen, että kuka tahansa voi päästä käsiksi kyseiseen olioon?

  30. Ratkaistaan ongelma! • Mitä jos loisimme luokkamuuttujan joka olisi osoitin luokan tyyppiseen olioon? • jos osoitin = 0, yhtään oliota ei ole vielä luotu • jos osoitin != 0, olio on jo luotu • Voisimme vielä luoda luokkafunktion (getInstance), joka palauttaisi osoittimen yhteen ja ainoaan olioon • jos luokkamuuttujaosoitin = 0, funktio loisi uuden olion ja palauttaisi sen osoitteen • jos olio olisi jo olemassa, funktio palauttaisi sen osoitteen

  31. Singleton Pattern class Singleton {public:static Singleton *get_instance();protected: Singleton(); Singleton( const Singleton& s);private:static Singleton *instance;};Singleton::instance = 0; Singleton *Singleton::get_instance() {if ( instance == 0 ) { instance = new Singleton; }return instance;} «Singleton» theInstance getInstance Company theCompany Company «private» getInstance if (theCompany==null) theCompany= new Company(); return theCompany;

  32. Mitä tuli tehtyä? • Käytimme luokkamuuttujaa ja luokkafunktiota fiksusti yhteen • nyt voimme olla varmoja, että ei ole koskaan mahdollista luoda enempää kuin yksi olio kyseistä tyyppiä • Olioon on kuitenkin todella helppo päästä käsiksi • Loimme itse asiassa yhden yleisimmistä Design Patterneista (singleton)!

  33. Missä mennään? • C++ tekniikoita • Nimiavaruus • Luokkamuuttujat • Luokkafunktiot • Operaattorien uudelleenmäärittely • Ystäväfunktiot • Ystäväluokat • Virhetilanteet ja poikkeukset • Syitä poikkeuksiin • Virhetilanteisiin reagointi • Virhehierarkiat • Poikkeuksien heitto ja sieppaaminen • Poikkeukset ja oliot • Yhteenveto

  34. Operaattoreiden uudelleenmäärittely • Olemme tottuneet käyttämään operaatioita kuten +, -, ==, *, /, jne… • Nämä ovat itse asiassa funktioita! • Tällaisia funktioita vain kutsutaan hieman eri tavalla! • Esimerkki x + 7; • Esitetty ihmiselle helpossa muodossa • Voidaan kuitenkin ajatella myös muodossa +(x,7), missä: • ‘+’ on funktion nimi • x,7 ovat argumentit • Funktio paluattaa argumenttiensa summan

  35. Operaattorien uudelleenmäärittely • Olisi kiva, jos voisimme operoida omiakin olioita tutuilla operaattoreilla! • Olioiden tietojen käsittely operaattorien avulla on ihmiselle luontevampaa • Kääntäjä ei voi kuitenkaan tietää miten tuttuja operaatioita voidaan soveltaa oikein eri tyyppisten luokkien kanssa  Luokan kirjoittajan tulisi itse määritellä miten operaatiot tulisi suorittaa.

  36. Uudelleenmääriteltävät operaattorit C++ kielessä + - * / % ^ & | ~ ! = < > += -= *= /= %= ^= &= |= << >> >>= <<= == != <= >= && || ++ -- ->* , -> [] () new delete

  37. Harjoituksia • Mitä seuraavien operaatioiden pitäisi mielestäsi tehdä? • PipeClient myPipe;myPipe.open(this);myPipe << “heihei”; • string kokoNimi = “Sami” + “Jantunen”; • Time myTime1,mytTime2, elapsedTime;myTime1.now();myTime2.now();elapsedTime=myTime2 - myTime1;

  38. Operaattorien uudelleenmäärittely Perusteet • Hyvin samanlainen funktioiden uudelleenmäärittelyn kanssa • Operaattori on itse asiassa funktion “nimi” • Kaksi eri toteutustapaa • Jäsenfunktiona • Tavallisena funktiona • Joidenkin operaattorien uudelleenmääritys on pakko olla toteutettuna jäsenfunktiona: • sijoitusoperaattori = • taulukon indeksointioperaattori [] • funktiokutsu () • jäsenen osoitusoperaattori ->

  39. Ohjeita uudelleenmäärittelystä • Määrittele, jos se selventää ohjelmaa • Älä uudelleenmäärittele siten, että operaattorin merkitys ei ole itsestäänselvä • Palauta operaattorifunktiosta aina kyseisen luokan tyyppinen olio

  40. Operaattorin uudelleenmäärittely jäsenfunktiona • Määritellään Mjono-luokalle jäseneksi ‘+’ –operaattori:const Mjono operator +(const Mjono& teksti); • Huomaa vain yksi argumentti! • Operaation toinen osapuoli on olio jota kutsutaan • Käyttöesimerkki:Mjono sukuNimi = “Jantunen”; Mjono etuNimi = “Sami”; Mjono kokoNimi = etuNimi + sukuNimi;

  41. Operaattorin uudelleenmäärittely jäsenfunktiona • Esitetty yhteenlasku voidaan esittää funktiokutsuna muodossa:kokoNimi = etuNimi.+(sukuNimi); kutsuttava olio funktion nimi argumentti

  42. Ongelma! • Miksi seuraava ei onnistu?:char cMjono[] = {“Hello”};Mjono mjono(“World!”);Mjono mJonoKaksi = cMjono + mMjono; //Virhe

  43. Syy ongelmaan • cMjono on C-tyyppinen merkkijono • sillä ei voi olla jäsenfunktioita • se ei voi suoriutua komennosta: cMjono.operator+(mMjono) Mjono mJonoKaksi = cMjono + mMjono; //Virhe kutsuttava olio funktion nimi argumentti

  44. Määritetään funktio:const Mjono operator +( const Mjono& teksti1, const Mjono& teksti2); Ratkaisu • Loogisesti ajateltuna edellä mainittu kahden merkkijonon yhteen liittäminen tulisi onnistua!!! Mikä olisi ongelmaan ratkaisu?

  45. Mitä tuli tehtyä? • Uudelleenmäärittelimme ‘+’ –operaattorin tavallisena funktiona:const Mjono operator +( const Mjono& teksti1, const Mjono& teksti2); • Yhden argumentin sijasta annamme kaksi argumenttia • C-tyyppinen merkkijono voidaan muuntaa sopivan tyyliseksi oikeanlaisella rakentajalla

  46. Vielä pieni ongelma • Tavallisina funktioina toteutetut operaattorien uudelleenmäärittelyt ovat tehottomia • Olioiden tietoihin voi päästä käsiksi vain olioiden julkisten rajapintojen kautta (get-metodit, muunnosmetodit) • Ylimääräistä työtä! Olisi parempi, jos operaattorin uudelleenmäärittelyn toteuttavassa funktiossa päästäisiin käsiksi käsiteltävien olioiden tietoihin suoraan

  47. Missä mennään? • C++ tekniikoita • Nimiavaruus • Luokkamuuttujat • Luokkafunktiot • Operaattorien uudelleenmäärittely • Ystäväfunktiot • Ystäväluokat • Virhetilanteet ja poikkeukset • Syitä poikkeuksiin • Virhetilanteisiin reagointi • Virhehierarkiat • Poikkeuksien heitto ja sieppaaminen • Poikkeukset ja oliot • Yhteenveto

  48. Ystäväfunktio • Luokalla on mahdollisuus määritellä joukon funktioita “ystävikseen” • Luokan ystäväfunktioiden koodi pääsee käsiksi myös luokan olioiden private-osiin. • ystäväfunktioilla on käytännössä katsoen samat oikeudet luokan olioihin kuin luokan omilla jäsenfunktioilla • Ystäväfunktiot ei kuitenkaan pääse käsiksi olioiden this-osoittimeen

  49. Ystäväfunktion määrittely • Ystäväfunktio määritellään kirjoittamalla varattu sana friend ja sen jälkeen funktion koko nimi • Kyseinen esittely EI tee ystäväfunktiosta luokan jäsenfunktiota! • kyseessä on täysin erillinen normaali funktio, jolle vain sallitaan pääsy luokan olioiden private-osaan

  50. ‘+’-operaattorin uudelleenmäärittely ystäväfunktiona • class Mjono • { • public: • //rakentajat • Mjono(); • Mjono(const char *const); • Mjono(const Mjono &) • ~Mjono; • Mjono operator+(const Mjono&); • friend Mjono operator+(const Mjono&, const Mjono&); • . • . • .

More Related