Tie koodariksi

C++-ohjelmointi

Luku 12: Tietueet ja luokat

Tietue

Tietue on muuttuja, jonka sisällä voi olla useita nimettyjä arvoja eli kenttiä. Tietuetyyppi luodaan sanan struct avulla, minkä jälkeen sitä voi käyttää koodissa.

Esimerkiksi seuraavassa tietueessa piste on kaksi int-kenttää x ja y: pisteen x- ja y-koordinaatti. Koodi luo pisteen (1,3) ja muuttaa sitten x-koordinaattia.

struct piste {
    int x, y;
};

int main() {
    piste a = {1,3};
    cout << a.x << " " << a.y << "\n"; // 1 3
    a.x = 2;
    cout << a.x << " " << a.y << "\n"; // 2 3
}

Tietueen käsittely

Tietue käyttäytyy kopioinnissa samalla tavalla kuin tavallinen muuttuja. Esimerkiksi seuraavassa koodissa a:n sisältö kopioidaan b:hen eikä b:n muuttaminen myöhemmin vaikuta a:han.
piste a = {1,3};
piste b = a;
b.x = 2;
cout << a.x << " " << a.y << "\n"; // 1 3
cout << b.x << " " << b.y << "\n"; // 2 3
Seuraavassa koodissa p on osoitin a:han, jolloin muutos tapahtuu a:ssa. Merkintä -> viittaa osoittimen kautta tietueen kenttään, ja p->x tarkoittaa samaa kuin (*p).x.
piste a = {1,3};
piste *p = &a;
p->x = 2;
cout << a.x << " " << a.y << "\n"; // 2 3
cout << p->x << " " << p->y << "\n"; // 2 3

Luokka ja olio

Tietueen osana voi olla kenttien lisäksi funktioita, joiden avulla voi käsitellä kenttiä. Tällöin tietueen määrittelyä kutsutaan nimellä luokka (class) ja tietuemuuttujaa kutsutaan nimellä olio (object).

Esimerkiksi seuraava koodi määrittelee luokan laskuri, jossa on laskurin arvoa säilyttävä kenttä x sekä kaksi funktiota: kasvata lisää arvoa yhdellä ja arvo palauttaa arvon.

struct laskuri {
    int x = 0;

    void kasvata() {
        x++;
    }

    int arvo() {
        return x;
    }
};

int main() {
    laskuri a;
    cout << a.arvo() << "\n"; // 0
    a.kasvata();
    cout << a.arvo() << "\n"; // 1
    a.kasvata();
    cout << a.arvo() << "\n"; // 2
}
Luokan ideana on, että se kokoaa yhteen johonkin kokonaisuuteen liittyvät tiedot ja niitä käsittelevät funktiot. Tämä on olio-ohjelmoinnin lähtökohta.

Konstruktori

Konstruktori on erikoisfunktio, joka suoritetaan olion luomishetkellä. Konstruktorin nimenä on luokan nimi, eikä sillä ole tyyppiä. Esimerkiksi seuraava konstruktori tulostaa rivin tekstiä:
struct testi {
    testi() {
        cout << "Moikka!\n";
    }
};

int main() {
    testi t; // tulostaa "Moikka!"
}
Tavallinen konstruktorin käyttökohde on olion alustaminen sen luomisen yhteydessä. Luokalla voi olla useita konstruktoreita, joilla voi olla eri parametreja.

Esimerkiksi seuraavassa luokassa on kaksi konstruktoria. Ensimmäisessä konstruktorissa ei ole parametria, jolloin alkuarvo on 0. Toisessa konstruktorissa parametrina annetaan alkuarvo.

struct laskuri {
    int x;

    laskuri() {
        x = 0;
    }

    laskuri(int a) {
        x = a;
    }

    // ...
};
Seuraava koodi esittelee asiaa:
laskuri a;
laskuri b(5);
cout << a.arvo() << " " << b.arvo() << "\n"; // 0 5
Tässä on vielä lyhempi tapa toteuttaa sama asia alustuslistan avulla:
struct laskuri {
    int x;

    laskuri() : x(0) {}

    laskuri(int a) : x(a) {}

    // ...
};

Luokan osien näkyvyys

Seuraavassa luokassa kenttä x on yksityinen (private), mikä tarkoittaa, että sitä voi käyttää vain luokan sisällä. Funktiot ovat sen sijaan julkisia (public), jolloin niitä voi käyttää myös luokan ulkopuolella.
struct laskuri {
private:
    int x = 0;

public:
    void kasvata() {
        x++;
    }

    int arvo() {
        return x;
    }
};
Koska funktiot ovat julkisia, voimme käyttää niitä kuten ennenkin:
int main() {
    laskuri a;
    a.kasvata();
    cout << a.arvo() << "\n";
}
Kuitenkaan emme pääse enää käsiksi suoraan kenttään x:
int main() {
    laskuri a;
    a.x++; // virhe!
    cout << a.x << "\n"; // virhe!
}
Tässä on ideana, että luokan julkiset funktiot tarjoavat rajapinnan, jonka avulla käytämme oliota, emmekä pääse käsiksi luokan sisäiseen toteutukseen.