h1. Abstraktit luokat
Yksi olio-ohjelmoinnin perusajatuksista on abstrahointi (abstraction), eli asioiden käsitteleminen abstraktilla tasolla. !abstractKantaĺuokka.png|border=1!
h3. Esimerkki 1. Abstrakti luokka.
{code}
class CElain
{
public:
virtual ~CElain() {} // Toteutus tulee olla (tyhjäkin käy).
virtual void Aantele() = 0; // Aito virtuaalifunktio.
};
{code}
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.
h3. Esimerkki 2. Abstraktin luokan toteuttava luokka.
{code}
class CKissa : public CElain
{
public:
void Aantele() {
printf("Miau!\n");
}
};
{code}
Tässä peritään CElain-luokka ja toteutetaan sen aito virtuaalifunktio _Aantele()_.
h2. 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].
h3. Esimerkki 3. Olioiden käsittely abstraktin (kanta)luokan avulla.
CElain:
{code}
// Abstrakti luokka, eli kantaluokka.
class CElain
{
public:
virtual ~CElain() {} // Toteutus tulee olla (tyhjäkin käy).
virtual void Syo() = 0; // Aito virtuaalifunktio.
};
{code}
CKissa:
{code}
// CElain-kantaluokan toteuttava luokka.
class CKissa : public CElain
{
public:
void Syo() {
printf("Kissa syo kalaa...\n");
}
};
{code}
CKoira:
{code}
// CElain-kantaluokan toteuttava luokka.
class CKoira : public CElain
{
public:
void Syo() {
printf("Koira syo lihaa...\n");
}
};
{code}
CElaintarha:
{code}
// 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...
}
}
};
{code}
{code}
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();
}
{code}
Esimerkki tulostaa seuraavat rivit:
{noformat}
Kissa syo kalaa...
Koira syo lihaa...
Kissa syo kalaa...
Koira syo lihaa...
Kissa syo kalaa...
{noformat}
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. |