Älykkäät osoittimet¶
C++:n älykkäät osoittimet (smart pointer) ovat kirjastotietotyyppejä, jotka automatisoivat dynaamisesti varatun muistin vapauttamisen sen jälkeen, kun kukaan ei enää viittaa siihen. Selkokielellä sanottuna siis: varattu muisti vapautetaan automaattisesti, kun ohjelmassa ei enää ole yhtään (älykästä) osoitinmuuttujaa, joka osoittaa kyseiseen muistialueeseen.
Dynaamiseen muistinhallintaan voitaisiin käyttää myös tavallisia osoittimia, mutta silloin ohjelmoijan vastuulle jäisi myös muistin vapauttaminen, mikä helposti unohtuu. Tällä kurssilla dynaamiseen muistinhallintaan käytetään vain älykkäitä osoittimia.
Älykkäiden osoittimien käyttö vaatii ohjeman alkuun rivin
#include <memory>
josta saadaan käyttöön tyypit
shared_ptr
unique_ptr
weak_ptr
Tällä kurssilla niistä tutustutaan vain shared_ptr-tyyppiin.
Jaettujen osoittimien ominaisuuksia¶
Lähes kaikki
operaatiot (unaarinen *, ->, vertailu ja tulostus), jotka
toimivat tavallisille osoittimille, toimivat myös
shared_ptr-osoittimille. Suurimpana erona on, että operaattorit
++ ja -- eivät toimi shared_ptr-osoittimille. Lisäksi
sijoitus toimii toisesta samantyyppisestä shared_ptr-osoittimesta.
Käydään vielä läpi muutama muu
shared_ptr-olioiden hyödyllinen ominaisuus,
joille saattaa joskus tulla tarvetta:
Jos
shared_ptr-osoittimesta pitää saada muistiosoite normaalina C++-osoittimena, se tapahtuuget-metodilla:shared_ptr<double> shared_double_ptr = make_shared<double>( 1.0 ); ... double *normaali_double_ptr = nullptr; ... normaali_double_ptr = shared_double_ptr.get();
shared_ptr-osoittimeen ei voi sijoittaa=-operaattorilla normaaliosoitinta.shared_ptr-osoittimeen voi kuitenkin sijoittaanullptr:in.shared_ptr-osoitinta ei voi vertailla suoraan normaaliosoittimeen. Vertailu onnistuu kuitenkin, josshared_ptr-osoitin muutetaanget-metodilla normaaliosoittimeksi, esimerkiksi:if(normaali_osoitin == shared_osoitin.get()) { ... }
shared_ptr-osoitinta voi vertaillanullptr:iin.
Tyypillä shared_ptr on yksi hankala ominaisuus:
Jos niiden avulla muodostetaan “silmukka”, muistia ei koskaan vapauteta:
#include <iostream>
#include <memory>
using namespace std;
struct Testi
{
// muita kenttiä
// ···
shared_ptr<Testi> osoite;
};
void fun()
{
shared_ptr<Testi> oso1 = make_shared<Testi>( ... );
shared_ptr<Testi> oso2 = make_shared<Testi>( ... );
oso1->osoite = oso2;
oso2->osoite = oso1;
}
Edellisestä on hyvä piirtää kuva, jotta ymmärtää kyseessä olevan
eräänlainen muna-ennen-kanaa -ongelma.
Nyt oso1:n osoittamaa muistia
ei voida vapauttaa, koska osoitin oso2->osoite osoittaa siihen.
Mutta toisaalta oso2:n osoittamaa muistia ei myöskään voi
vapauttaa, koska oso1->osoite osoittaa siihen.
Esimerkki: Varaukset¶
Tarkastellaan tämän viikon esimerkkiä examples/06/reservations/.
Siinä käsitellään kirjaston kirjojen varaamista viitelaskureiden avulla.
Kun kirja varataan, laitetaan uusi jaettu osoitin osoittamaan kirjaa.
Tällöin viitelaskurin arvo kertoo varausten määrän.

Määritellään
shared_ptr-tyyppinen osoitinmuuttuja, johon voidaan tallentaaint-tyyppisen muuttujan muistiosoite. Alustetaan se osoittamaan dynaamisestinew’llä varattuun muuttujaan, jonka arvo on 1.