Abstraktit luokat

Yksi olio-ohjelmoinnin perusajatuksista on abstrahointi (abstraction), eli asioiden käsitteleminen abstraktilla tasolla.

Esimerkki 1. Abstrakti luokka.

class CElain
{
public:
    virtual ~CElain() {}        // Toteutus tulee olla (tyhjäkin käy).
    virtual void Aantele() = 0; // Aito virtuaalifunktio.
};

Yllä esitellään luokka CElain, jossa on yksi aito virtuaalifunktio, Aantele(). Tämä tarkoittaa, että CElain luokasta ei voida luoda oliota. Aantele()-metodin toteutus tulee olla toteutettuna perivässä luokassa.

Esimerkki 2. Abstraktin luokan toteuttava luokka.

class CKissa : public CElain
{
public:
    void Aantele() {
        printf("Miau!\n");
    }
};

Tässä peritään CElain-luokka ja toteutetaan sen aito virtuaalifunktio Aantele().

Abstraktien luokkien käsittely

Abstrakteista luokista ei voida luoda olioita, ainoastaan niiden ei-abstrakteista perillisistä, joita myös kutsutaan (abstraktin luokan) toteuttaviksi luokiksi. Abstraktit luokat toimivat rajapintana (interface) toteuttavalle luokalle. Kts polymorfismi.

Esimerkki 3. Olioiden käsittely abstraktin (kanta)luokan avulla.

CElain:

// Abstrakti luokka, eli kantaluokka.
class CElain
{
public:
    virtual ~CElain() {}    // Toteutus tulee olla (tyhjäkin käy).
    virtual void Syo() = 0; // Aito virtuaalifunktio.
};

CKissa:

// CElain-kantaluokan toteuttava luokka.
class CKissa : public CElain
{
public:
    void Syo() {
        printf("Kissa syo kalaa...\n");
    }
};

CKoira:

// CElain-kantaluokan toteuttava luokka.
class CKoira : public CElain
{
public:
    void Syo() {
        printf("Koira syo lihaa...\n");
    }
};

CElaintarha:

// Luokka johon voidaan tuoda CElain-luokan perillisiä.
class CElainTarha
{
protected:
    CElain* Elaimet[100];   // 100 CElain-tyyppistä osoitinta taulukossa
    int ElainLaskuri;       // laskuri, joka kertoo monta Elainta on lisätty ylläolevaan taulukkoon.

public:
    CElainTarha() :
        ElainLaskuri(0)     // nollataan ElainLaskuri
    {
    }

    ~CElainTarha()
    {
        // Vapautetaan Elaimet-taulukon olioiden varaama muisti:
        for (int i=0; i<ElainLaskuri; i++) {
            delete Elaimet[i];
        }
    }

    bool LisaaElain(CElain* elain)
    {
        if (ElainLaskuri < 100) {
            Elaimet[ElainLaskuri] = elain;  // Lisätään parametrina saatu elain-olio Elaimet-taulukkoon
            ElainLaskuri++;
            return true;
        } else {
            return false;
        }
    }

    void SyotaElaimet() {
        for (int i=0; i<ElainLaskuri; i++) {
            Elaimet[i]->Syo();               // Kutsutaan kaikkien Elaimien Syo()-funktiota. Huom! '->' käytettään '.' sijaan silloin kun käsitellään osoittimia...
        }
    }
};
int main()
{
    CElainTarha ElainTarha;

    ElainTarha.LisaaElain(new CKissa);
    ElainTarha.LisaaElain(new CKoira);
    ElainTarha.LisaaElain(new CKissa);
    ElainTarha.LisaaElain(new CKoira);
    ElainTarha.LisaaElain(new CKissa);

    ElainTarha.SyotaElaimet();
}

Esimerkki tulostaa seuraavat rivit:

Kissa syo kalaa...
Koira syo lihaa...
Kissa syo kalaa...
Koira syo lihaa...
Kissa syo kalaa...

Esimerkissä luodaan kantaluokka, CElain ja siitä periytetään kaksi perillistä, CKissa ja CKoira. Lisäksi luodaan CElainTarha-luokka, johon voidaan tuoda CElain-tyyppisiä osoittimia olioihin, riippumatta siitä, onko ne luotu CKissa vai CKoira -luokista. Tämä onnistuu siksi, että molemmilla luokilla on sama kantaluokka, CElain. Kutsumme lopuksi CElainTarhan metodia SyotaElaimet(), joka 'syöttää' jokaista eläintä, eli kutsuu sekä kissojen että koirien Syo()-metodeita.

Etuna tässä toteutustavassa on, että jatkossa voidaan luoda uusi CElain-luokan perillinen, esim. CKirahvi. Tästä luokasta tehty olio voidaan viedä CElainTarha-luokkaan, ilman että sen koodia tarvitsee muuttaa ollenkaan.

  • No labels
You must log in to comment.