- TituCPP 9. luento 29.11.
- Materiaali
- Esimerkit
- Virhetilanteet ja poikkeukset
- c++ faq lite, poikkeustenkäsittelyosio
- [ http://www.parashift.com/c++-faq-lite/exceptions.html ]
- Virheiden käsittelystä
- poikkeukset ovat yleensä (mutta ei välttämättä) exception-luokasta perittyjä olioita - heitetyn poikkeuksen mukana voidaan lähettää ylemmälle tasolle tietoa virhetilanteesta
- Myös omia poikkeusluokkia (voi käyttää myös perustyyppejä) voi määritellä - ei tarvitse (mutta yleensä kannattaa) olla perittyjä standardiluokista
- pyrkivät korvaamaan C-tyylisen virhetilanteen käsittelyn funktion paluuarvon testauksena
- poikkeuksia ei välttämättä kannata siepata heti havaitessa, vaan sillä tasolla, jolla on riittävästi tietoa virhetilanteen käsittelyyn
- toisaalta kaikkia poikkeuksia ei missään tapauksessa pidä "valuttaa" pääohjelmaan...
- käytännön ongelma: mikä on "oikea" virheenkäsittelytaso?
- poikkeus voidaan ajatella myös kontrolloituna goto-lauseena...
- Virheet ja muistinkäsittely
- pino-oliot tuhotaan automaattisesti poikkeuksen heiton yhteydessä
- jos tuhottava pino-olio heittää destruktorissa poikkeuksen (toisen poikkeuksenkäsittelyn aikana), ohjelman suoritus lopetetaan
- dynaamisia olioita _ei_ tuhota automaattisesti
- Poikkeusmääreet
- hyvä käytäntö (esim. javassa pakollinen): merkitse poikkeusmääreillä, mitä poikkeuksia funktion odotetaan heittävän
- RAII-idiomi (resource aquisition is initialization - resurssin varaaminen on alustamista)
- resurssi: esim. muisti, tiedostot, ikkunakahvat, tietokantayhteydet => jotain, minkä varaamisesta ja tuhoamisesta käyttäjä vastaa
- "c-tyylillä" tehty poikkeuskäsittely näyttäisi seuraavalta:
- // huonoa koodia
try {
fopen();
...
}
{catch ...} {
fclose(); // jos jokin meni vikaan
}
fclose(); // tavanomainen tilanne
- javassa vastaavassa käytetään try-finally -rakennetta (finally-lohko suoritetaan aina tryn jälkeen, vaikka heitettäisiin poikkeus) => lähellä "c-tyyliä":
- // pseudojavaa
try {
fopen();
...
}
finally {
fclose(); // suoritetaan aina
}
fclose()
- parempi tapa: käytetään paikallisia muuttujia, jotka omistavat resurssin
- resurssi varataan konstruktorissa (=varaaminen on alustamista) ja vapautetaan destruktorissa
- jos resurssin käytön aikana sattuu poikkeus, pino puretaan automaattisesti ja destruktoria tulee kutsuttua
- varsinainen poikkeuksenkäsittely voidaan tehdä ohjelman "ylemmällä" tasolla, jolloin todennäköisesti tiedetään paremmin, miten tilanteeseen tulisi reagoida
- auttaa välttämään suurta määrää try/catch-lohkoja
- ks. jon hanna: the raii programming idiom
- [ http://www.hackcraft.net/raii/ ]
- "fiksuista" osoittimista
- auto_ptr
- esimerkki raii-idiomin käytöstä
- alustetaan osoittimella ja sisältö voidaan lukea kuten osoittimen sisältö (kuormitettu *-operaattori)
- auto_ptr:n viittaama olio tuhoutuu automaattisesti viittausalueen lopussa
- huom. ei käy stl-säiliöiden alkioksi (vector) - sijoitukset eivät toimi oikein, koska omistajuus muuttuu kopioitaessa
- tr1-määrityksessä (tod. näk. osa tulevaa c++ -standardia) lisäksi:
- [ http://aristeia.com/EC3E/TR1_info_frames.html ]
- jaettu osoitin (käyttää viitelaskuria)
- heikko osoitin
- Kerho: tiedostonkäsittely ja päätesyöttö
- Tiedostonkäsittely Kerhoon: lisää Jäsen- ja Harrastusluokkiin toString- ja parse-metodit ja tietorakenneluokkiin tiedoston luku ja luettujen olioiden lisääminen rakenteeseen
- [ http://www.mit.jyu.fi/vesal/kurssit/cpp/kerho/talletus.2 ]
- Päätesyötön lähtökohta: kysy_tiedot -metodin kehittäminen
- Indeksoidut kentät - keino käyttöliittymän ja sovelluslogiikan erottamiseen päätesyötössä
- Ei vaadi välttämättä omaa kenttäluokkaa - rajapinnan kannalta indeksoidut saanti- ja asetusmetodit riittävät
- Tietojen siirto näytön ja jäsenen välillä kannattaa pitää merkkijonomuotoisena (tyyppimuunnokset vain jäsenessä)
- eka: ensimmäinen _käyttäjälle näytettävä_ kenttä (id-numerot ennen ekaa)
- Toisesta tietorakenteesta haettavat valintalistat aiheuttavat hieman lisävaivaa
- toimintaa siirrettävä jäsentasolta kerhotasolle
- esim. harrastusten kysely on helppoa toteuttaa ilman lisätoimintoja (oleellisesti sama koodi kuin jäsenen tietojen kysymisessä), mutta esim. genre- tai esittäjälistan tulostaminen syötettäessä tietoja cd-rekisteriin vaatii jo pohjaa hakujärjestelmälle.
- Esimerkki:kaupunkien lisääminen jäsenrekisteriin (tietokannan normalisointia)
- Tehdään uudet luokat Kaupungit ja Kaupunki (sisältää vain nimen ja id-numeron)
- Rakenteen muutoksen seurauksena Jäsen ei enää tiedä suoraan kotikaupunkiaan, vaan ainoastaan sen ID-numeron
- Koska Jäsen ei saa olla suoraan tietoinen Kaupunki-luokasta, Jäsen ei tämän jälkeen mm. pysty tulostamaan itseään käyttäjille kaupunkitiedon kanssa => tulostustoiminto siirrettävä Kerho-luokkaan
- Lisäksi tietoja kysyttäessä käyttäjän on saatava tarvittaessa nähdä kaupunkilista, josta valita jäsenen kotipaikka => monimutkaistaa näyttöluokkaa
- Toiminta kannattaa toteuttaa täysimääräisenä vasta, kun käytössä on hakujärjestelmä - aluksi voidaan toteuttaa tietorakenne ja esim. jäsenen tulostus
- Periaate: siirretään kaupungin id-numero kenttiin, joita ei kysytä käyttäjältä silmukassa ja siirretään kysyminen suoraan näyttöön
- Käyttöliittymä esim: käyttäjä voi kirjoittaa kaupungin nimen tai pyytää listan, josta valita
- Jos kirjotettua nimeä ei löydy kaupungeista, varmistetaan kaupungin lisääminen
- Asetetaan jäsenen kapunkiID lisätyn/etsityn kaupungin ID:n mukaisesti
- Esimerkki: kerho/lukemine_71
- [ http://people.cc.jyu.fi/~minurmin/ohj2/materiaali/kerho/lukemine_71/ ]
- Harrastusten kysely? Valintalistan tulostaminen?
- Mallit (templatet) ja geneerisyys
- Abstraktiotason nostamista!
- Mallit vs abstraktit luokat
- Esim. taulukkoluokka
- Ilman malleja vaatii typecasteja ja yhteisen yliluokan määritystä säilöttäville olioille
- Taulukko voi sisältää samanaikaisesti useisiin eri aliluokkiin kuuluvia olioita (usein tämä voi olla hyödyllistäkin)
- Funktiomallit
- Luokkamallit
- Jäsenfunktiomallit
- Raa'asti yksinkertaistaen tyyppiturvallinen tapa käyttää makroja
- Malleista käännetään versioita eri tyypeille tarpeen mukaan
- Seuraus: mallin lähdekoodin on oltava tiedossa käännösvaiheessa (mallin koodi kirjoitetaan otsikkotiedostoon, ellei käytetä export-avainsanaa)
- STL: kattava esimerkki geneerisyyden soveltamisesta
- STL - Standard Template Library
- säiliöt (vector, set, map, ...)
- huom. algoritmien kompleksisuudet eri säiliöluokilla vaihtelevat (esim. lista vs vector)
- vaatimuksia säilöitä käyttäville olioille:
- kopionmuodostin ja sijoitusoperaattori
- assosiatiivisilla säiliöillä sekä avaimille että arvoille - lisäksi avainten tulee olla järjestettävissä
- => viitteitä (sijoitus ei muuta viitettä toiseen olioon) tai auto_ptr:ää (sijoitus siirtää olion omistuksen) ei voi käyttää stl-säiliöihin
- sarjat
- vector ("dynaaminen taulukko")
- nopea käsittely indeksioperaattorilla []
- nopea lisäys ja poisto loppuun (push_back ja pop_back)
- deque (jono)
- molempiin päihin tehdyt lisäykset (push_front, push_back) ja poistot (pop_front, pop_back) vakioaikaisia
- list (linkitetty lista)
- Dynaaminen rakenne: lisääminen ja poisto vakioaikaista (jos tiedetään lisäys/poistopaikka), haun kompleksisuus lineaarinen
- ei indeksointioperaattoria (läpikäynti iteraattoreilla)
- Indeksointi tehotonta (lineaarinen kompleksisuus, vrt. taulukko)
- Perusmalli: alkio sisältää viitteen seuraavaan ja edelliseen
- assosiatiiviset säiliöt
- alkioiden lisäysjärjestyksellä ei merkitystä, käsittely avainolioilla (map-luokissa avain viittaa lisäksi
erillisiin arvo-olioihin, set säilöö vain avaimia)
- lisäyksellä, poistolla ja haulla logaritminen kompleksisuus
- koko tietorakenteen läpikäynti iteraattoreilla
- set - joukko
- Ei salli alkioiden (avainten) moninkertaisia esiintymiä
- Voidaan toteuttaa esim. hajautustauluna tai binääripuuna
- multiset - "laukku" (bag), voi sisältää saman arvon moneen kertaan (lukumäärä kysyttävissä count-metodilla)
- map - assosiaatiotaulu, hakemisto (toteutus esim. hajautustauluna tai binaaripuuna)
- (avain,arvo)-pareja, avain on yksilöllinen
- Avaimet varastoidaan joukkoon, kustakin avaimesta linkki arvoon
- template-parametreina annetaan avaimen ja arvon tyypit
- avaimella tulee olla järjestysoperaattori
- multimap - vrt. multiset
- ks. rakenteet.pdf
- [ http://www.jyu.fi/static/titu/cppolio/materiaali/rakenteet.pdf ]
- säiliösovittimet
- rajoitteita/muuntimia säiliön rajapinnoille "perinteisiä" tietorakenteita varten - sisäinen toteutusrakenne on vaihdettavissa
- stack - pino (yleensä toteutettu dequella) - push lisää alkion pinon päälle ja pop poistaa sen
- queue - jono (yleensä rajoitettu versio dequesta - push lisää alkion jonon loppuun ja pop poistaa alusta)
- priority_queue - prioriteettijono (yleensä vektori) - alkioiden poisto suuruusjärjestyksessä
- iteraattorit
- kohti iteraattoreita: Kerhon eka ja seuraava
- [ http://www.mit.jyu.fi/vesal/kurssit/cpp/kerho/jako.15/kerho.h ]
- ongelmia
- läpikäynnin tilan kytkeminen rakenteeseen eka ja seuraava eivät voi olla const-metodeja
- tietorakenteen läpikäynti samanaikaisesti moneen kertaan
- monesti tietorakennetta käsittelevissä silmukoissa ei olla kiinnostuneita yksittäisistä indekseistä, vaan siitä, että koko tietorakenne tulee käsiteltyä
- ratkaisu: uusi luokka, johon kapseloidaan vain läpikäyntilogiikka
- iteraattorit jaettu niiden ilmaisuvoiman ja käyttötarkoituksen mukaisiin luokkiin
- input iterator - voin luku ja seuraava
- output iterator - vain kirjoitus ja seuraava
- forward iterator - luku, kirjoitus ja seuraava
- bidirectional iterator - kaksisuuntainen
- random access iterator - []-operaattori
- iteraattoreita voi käyttää samaan tapaan kuin osoittimia c-taulukoilla (iteraattorin tyypistä riippuen kuormitetut *, ->, ++ ja -- -operaattorit, hajasaanti-iteraattorilla myös [])
- standardisäiliöihin määritelty valmiiksi iteraattoriluokat (esim vector<int>::iterator) ja metodit begin() ja end(), jotka palauttavat iteraattorin ensimmäiseen alkioon ja paikkaan viimeisen alkion ohi
- Huom. osoittimien tapaan iteraattori voi muuttua kelvottomaksi iterattorin viittaamaa tietorakennetta muutettaessa
- iteraattoriluokkia on mahdollista määritellä itsekin, mutta tämä on harvoin tarpeen (ellei haluta tehdä alusta alkaen mahdollisimman tehokasta ja stl-vakioluokista poikkeavaa omaa tietorakennetta tai rakennetta)
- iteraattorisovittimet (käyttäytyminen poikkeaa tavallisista iteraattoreista)
- käänteisiteraattorit (rbegin, rend -metodit) - läpikäynti käänteisessä järjestlyksessä
- lisääjät (inserter) - kirjoittaminen lisää paikkaan uuden alkion, ei muuta
- virtaiteraattorit (esim. ostream_iterator(cout)) - lukevat ja kirjoittavat tietovirtoihin