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
Pythonin synttarisankari-viite osoittaa samaan olioon kuin henkilo1
Viite synttarisankari osoittaa samaan olioon kuin henkilo1.

Kun suoritetaan sijoitus synttarisankari = henkilo2, muuttuja viittaa tämän jälkeen henkilo2-olioon:

synttarisankari = henkilo2
Pythonin synttarisankari-viite osoittaa nyt samaan olioon kuin henkilo2
Sijoituksen jälkeen 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.

C++:n viite osoittaa kokonaisluku-muuttujaan, jonka arvo on 100
Viite osoittaa 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;
Osoitin kokonaislukuosoitin osoittaa kokonaisluku-muuttujaan, jonka arvo on 42
Osoitin sisältää 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;
Osoitin synttarisankari osoittaa olioon henkilo1
Osoitin synttarisankari osoittaa henkilo1-olioon.

Sen voi myöhemmin asettaa osoittamaan toiseen olioon:

synttarisankari = &henkilo2;
Osoitin synttarisankari osoittaa nyt olioon henkilo2
Uuden sijoituksen jälkeen osoitin viittaa 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;?

&-operaattori muodostaa tässä muuttujan luku muistiosoitteen.

Mitä *p tarkoittaa, kun p on kelvollinen osoitin?

Osoittimen dereferointi *p viittaa siihen arvoon, johon p osoittaa.