812347A Olio-ohjelmointi, 2015 syksy 2. vsk X Poikkeusten käsittelystä
Sisältö 1. Yleistä poikkeusten käsittelystä 2. Poikkeuskäsittelyn perusteita C++:ssa 3. Standardissa määritellyt poikkeukset 4. Poikkeusvarmuus käsittelystä 2
X.1. Yleistä poikkeusten käsittelystä Poikkeukset (exception) ovat ohjelman suorituksenaikaisia poikkeustapauksia, jotka vaativat välitöntä käsittelyä, kuten dynaamisen muistin loppuminen Poikkeuksien avulla voidaan välittää virhetilanne virheen syntypaikasta paikkaan, jossa on tarvittavat voimavarat virheen käsittelemiseen Saadaan selvempää ja lyhyempää koodia Soveltuu erityisesti laajoihin ohjelmiin käsittelystä 3
X.1.1 Poikkeusten käsittelyn pääkomponentit Ohjelmanosa, jossa poikkeus tapahtuu Havaitaan poikkeama ja aiheutetaan poikkeus (raise) Keskeytetään normaali ohjelmansuoritus, kunnes poikkeus käsitelty C++:ssa poikkeus aiheutetaan (heitetään) throwlausekkeella Ohjelmanosa, jossa poikkeus käsitellään Käsittelijän löytämiseksi noustaan ohjelman kutsupinossa Kun poikkeus on käsitelty, ohjelman suoritus jatkuu normaalisti käsittelylohkon jälkeisestä lauseesta C++:ssa poikkeuksen käsittely toteutetaan (poikkeus siepataan) catch-lauseella käsittelystä 4
X.1.2 Standardissa määritellyistä poikkeustapauksista new-operaattorin kutsussa, jos ei ole saatavilla muistia STL:n säiliöiden käsittelyssä erilaisia poikkeuksia mm. Indeksirajojen ylittäminen jäsenfunktioiden kutsuissa Muistin loppuminen IOStream-kirjastoa käytettäessä, jos vuot asetettu käyttämään poikkeusten käsittelyä Poikkeusta ei aiheuteta Indeksirajojen ylittämisessä käytettäessä operaattoria [] sisäisille taulukoille tai STL:n säiliöille Tiedoston avauksen epäonnistuessa tai tiedoston loppuessa sitä luettaessa käsittelystä 5
X.2. Poikkeuskäsittelyn perusteita C++:ssa C++ poikkeuskäsittelyn peruselementit: 1. try-lohko, jonka sisällä tapahtuvat poikkeukset 2. throw-lause, joka aiheuttaa poikkeuksen 3. catch-lohkot, jotka käsittelevät poikkeukset Poikkeuksen tapahduttua käydään lävitse kutsupino, kunnes poikkeusta vastaava poikkeuskäsittelijä löytyy ja samalla vapautetaan automaattisesti paikalliset muuttujat (ei staattiset, ei globaalit, ei kekodynaamiset) Jos poikkeusta ei käsitellä missään vaiheessa, poikkeus välittyy käyttöjärjestelmälle Ohjelma kaatuu Jos poikkeus olion muodostimessa, oliota ei muodosteta käsittelystä 6
X.2.1 Poikkeuksen aiheuttaminen C++:n poikkeus aiheutetaan throw-lauseella Poikkeuksen tyyppi voi olla mikä tahansa määritelty tyyppi myös sisäinen kuten int, char* Poikkeuksen ei tarvitse periytyä tietystä kantaluokasta (kuten Javassa) Funktion ei tarvitse ilmoittaa määrittelyn yhteydessä mitä poikkeuksia se aiheuttaa Jokainen funktio tai luokan jäsenfunktio voi aiheuttaa minkä tahansa poikkeuksen tahansa milloin tahansa käsittelystä 7
X.2.2 try-lohko try-lohkoon voidaan liittää poikkeuksen käsittelijä try-lohkossa olevat lausekkeiden aiheuttamat poikkeukset joko Käsitellään try-lohkon yhteydessä määritellyssä käsittelijässä Välitetään kutsuvalle funktiolle try-lohkoon liittyy vähintään yksi käsittelijä käsittelystä 8
X.2.3 catch-lohko catch-lohkolla määritellään poikkeuskäsittelijä catch-lohkon määrittely catch (std::bad_alloc &ba) { Poikkeuksen tyyppi ja muuttujamääritys // muisti loppunut: käsittele Varsinainen lohko catch - lohkot käsitellään siinä järjestyksessä kuin ne on määritelty käsittelystä 9
X.2.4 Poikkeuksen sitominen käsittelijään Jos heitetty poikkeus on tyyppiä E ja catch-lohkon parametri on tyyppiä X, niin lohko käsittelee poikkeuksen, jos 1. X on samaa tyyppiä kuin E 2. X on luokan E yksikäsitteinen julkinen kantaluokka 3. X ja E ovat osoitintyyppejä, joiden tarkoitetyypeille pätee 1 tai 2 4. X on viite, jonka tarkoitetyypille pätee 1 tai 2 käsittelystä 10
Esimerkki I int main() { try { f(); // voi aiheuttaa minkä tahansa // poikkeuksen catch (std::bad_alloc &ba) { // muisti loppunut käsittelystä 11
Esimerkki II class IkaPoikkeus {; class IkaKuukausiVirheellinenPoikkeus : public IkaPoikkeus {; class IkaVuosiVirheellinenPoikkeus : public IkaPoikkeus {; class IkaPaivaVirheellinenPoikkeus : public IkaPoikkeus {; class Ika{ public: Ika(int paivia, int kuukausia, int vuosia); private: void calculatejd(long diff); unsigned long jddays; ; käsittelystä 12
Esimerkki II (2) Ika::Ika(int paivia, int kuukausia, int vuosia){ if (paivia < 0) throw IkaPaivaVirheellinenPoikkeus(); if (kuukausia < 0) throw IkaKuukausiVirheellinenPoikkeus(); if (vuosia < 0) throw IkaVuosiVirheellinenPoikkeus(); käsittelystä 13
Esimerkki II (3) int main(){ int pv,kk, v; do { cin.clear(); cin.ignore(1000,'\n'); cout << Anna ikä pv, kk, v << endl; cin >> pv >> kk >> v; while (cin.fail()); try{ Ika ika(pv, kk, v); catch (IkaPoikkeus& p){ cout << jokin parametri virheellinen ; return 0; käsittelystä 14
X.2.5 Yleinen poikkeuskäsittelijä Yleinen poikkeuskäsittelijä ottaa kiinni kaikki poikkeukset Sen muoto on catch ( ) Lohkossa ei voida tarkemmin tietää, mikä poikkeus on tapahtunut Jos määritellään, niin määriteltävä viimeisenä käsittelystä 15
Esimerkki int main() { try { f(); catch (...) { cout << Jokin poikkeus tapahtui << endl; cout << Ohjelman suoritus lopetetaan << endl; return 0; käsittelystä 16
X.3. Standardissa määritellyt poikkeukset Määritelty otsikkotiedostoissa new, exception ja stdexcept exception bad_exception bad_cast bad_alloc ios_base::failure exception... logical_error domain_error invalid_argument length_error out_of_range runtime_error range_error overflow_error underflow_error käsittelystä 17
X.4. Poikkeusvarmuus (Exception Safety) Poikkeustenkäsittelyä pidetään joskus virheellisesti suorasukaisena keinona lisätä luotettava virheiden käsittely ohjelmiin Poikkeusten käsittelyn lisääminen ohjelmaan voi Heikentää ohjelmien luotettavuutta Hankaloittaa ohjelmistokehitysprosessia Poikkeustenkäsittelyn ohjelmoinnissa hankalinta on kirjoittaa ohjelmistokoodi siten, että yksittäinen poikkeus voi välittyä throw kohdasta sen käsittelijälle luotettavasti, rikkomatta muita osia ohjelmasta siirtymisen aikana käsittelystä 18