Oliot, osoittimet ja taulukot
Johdattelua osoittimiin
Aiemmin tutustuimme STL:n iteraattoreihin, jotka toimivat monilta osin osoittimien tavoin. Olemme myös käsitelleet Pythonin ja C++:n viitteitä. Niiden toiminnassa on tärkeä ero: Pythonissa viitemuuttujan voi asettaa viittaamaan toiseen olioon, mutta C++:n viitettä ei voi kohdistaa uudelleen.
Pythonin viite ja C++:n viite
Pythonissa sijoitus muuttaa sitä, mihin muuttujan nimi viittaa:
henkilo1 = Henkilo("Kalle", 6) henkilo2 = Henkilo("Ville", 5) synttarisankari = henkilo1
synttarisankari osoittaa samaan olioon kuin henkilo1.Kun suoritetaan sijoitus synttarisankari = henkilo2, muuttuja viittaa tämän jälkeen henkilo2-olioon:
synttarisankari = henkilo2
synttarisankari osoittaa henkilo2-olioon.C++:n viite toimii eri tavalla. Kun viite on alustettu, sille tehty sijoitus muuttaa viitteen kohteen arvoa:
int kokonaisluku = 42; int& kokonaislukuviite = kokonaisluku; kokonaislukuviite = 100;
Sijoituksen jälkeen kokonaisluku-muuttujan arvo on 100. Viite viittaa yhä samaan muuttujaan.
kokonaisluku, jonka arvo on 100.C++:n viitettä ei voi asettaa uudelleen osoittamaan toiseen kohteeseen. Viite pitää alustaa heti määrittelyssä, ja se säilyy sidottuna samaan kohteeseen koko elinaikansa.
Osoitin on muuttuja, joka tallentaa osoitteen
C++:ssa on myös toinen epäsuoraan käsittelyyn tarkoitettu tyyppi: osoitin. Osoitin on muuttuja, jonka arvo on toisen muuttujan muistiosoite. Osoittimen tyyppi kertoo, minkä tyyppiseen kohteeseen se voi osoittaa.
Osoitin määritellään lisäämällä * kohdetyypin ja muuttujan nimen väliin. Osoite saadaan &-operaattorilla:
int kokonaisluku = 42; int* kokonaislukuosoitin = &kokonaisluku;
kokonaisluku-muuttujan osoitteen.*-merkkiä käytetään myös osoittimen kautta kohteen käsittelyyn eli osoittimen dereferointiin:
cout << *kokonaislukuosoitin << endl; // tulostaa 42 *kokonaislukuosoitin = 100; // muuttaa kokonaisluvun arvoksi 100
Tässä &kokonaisluku tarkoittaa muuttujan osoitetta, kun taas *kokonaislukuosoitin tarkoittaa osoittimen osoittamaa arvoa. Muuttujan määrittelyssä * kertoo osoitintyypin; lausekkeessa * dereferoi osoittimen.
Osoittimen voi asettaa osoittamaan toiseen kohteeseen
Toisin kuin C++:n viite, osoitin on tavallinen muuttuja, jonka arvoa voi vaihtaa:
Henkilo henkilo1("Kalle", 6); Henkilo henkilo2("Ville", 5); Henkilo* synttarisankari = &henkilo1;
synttarisankari osoittaa henkilo1-olioon.Sen voi myöhemmin asettaa osoittamaan toiseen olioon:
synttarisankari = &henkilo2;
henkilo2-olioon.Osoittimella pääsee olion jäseniin käsiksi joko ensin dereferoimalla sen ja käyttämällä pistettä ((*synttarisankari).tulosta()) tai tavallisemmin nuolioperaattorilla (synttarisankari->tulosta()).
Tyhjä osoitin ja turvallinen käyttö
Jos osoitin ei tällä hetkellä osoita mihinkään kelvolliseen kohteeseen, se voidaan asettaa arvoon nullptr:
Henkilo* synttarisankari = nullptr; if (synttarisankari != nullptr) { synttarisankari->tulosta(); }
Älä dereferoi nullptr-osoitinta. Tarkista osoitin ennen kuin käytät sitä kohteen käsittelyyn. Osoittimen täytyy myös osoittaa elossa olevaan olioon; jo tuhoutuneen paikallisen olion osoitteen säilyttäminen tekee osoittimesta virheellisen.
Tämä on vasta johdatus osoittimiin, ei kattava esitys niiden käytöstä. Seuraavassa Mölkky-tehtävässä on valmis pääohjelma, jossa käytetään osoitinta. Vaikka pääohjelmaa ei tarvitse muuttaa, tutki sitä osoittimen käyttöesimerkkinä. Osoittimia tarvitaan kurssilla myöhemmin useisiin tarkoituksiin, ja niiden yksityiskohtiin palataan perusteellisemmin.
Video: Osoittimet (12 min)
Videolla havainnollistetaan osoitinmuuttujan, tavallisen muuttujan ja viitteen eroja.
Avaa Osoittimet-video (12 min)
Kertaa: Osoittimet
Mitä operaattori & tekee lausekkeessa int* p = &luku;?
Mitä *p tarkoittaa, kun p on kelvollinen osoitin?