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.