02.01.2015 Views

Osa 5: Periytyminen ja polymorfismi - Tekniikan yksikkö - Oamk

Osa 5: Periytyminen ja polymorfismi - Tekniikan yksikkö - Oamk

Osa 5: Periytyminen ja polymorfismi - Tekniikan yksikkö - Oamk

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

T740103 Olio-ohjelmointi<br />

<strong>Osa</strong> 5: <strong>Periytyminen</strong> <strong>ja</strong> <strong>polymorfismi</strong><br />

© Jukka Jauhiainen OAMK <strong>Tekniikan</strong> yksikkö 2010<br />

12. <strong>Periytyminen</strong><br />

Johdantoa<br />

Käytännössä vähänkään laajemmissa ohjelmissa joudutaan laatimaan useita luokkia, joiden pitäisi pystyä<br />

välittämään tietoa toisilleen. Ohjelmien ylläpidon kannalta olisi lisäksi suotavaa, että samaa koodia ei ole<br />

useassa eri paikassa.<br />

Olio-ohjelmoinnissa luokat voidaan järjestää siten, että ne pystyvät <strong>ja</strong>kamaan yhteisiä tieto<strong>ja</strong> <strong>ja</strong> aliohjelmia.<br />

<strong>Periytyminen</strong> tarkoittaa, että jonkin ns. kantaluokan (base class) ominaisuuksia voidaan periyttää (inherit)<br />

kantaluokasta johdetulle luokalle (derived class). Kokonaisuudessa luokkien periytyminen on varsin<br />

monimutkaista. Tarkastellaan tällä kurssilla yksinkertaisinta tapausta eli ns. palveluliittymän periytymistä eli<br />

public-periytymistä. Tarkemmin periytyminen on käsitelty esimerkiksi Markun materiaalissa <strong>ja</strong> Hietasen<br />

kir<strong>ja</strong>ssa.<br />

Kun tietokoneohjelmalla pyritään mallintamaan jotain reaalimaailman ilmiötä, päädytään väistämättä<br />

määrittelemään erilaisia käsitteitä <strong>ja</strong> niiden välisiä suhteita. Yritäpä esimerkiksi mallintaa, mikä auto on. Pian<br />

joudut turvautumaan sellaisiin käsitteisiin kuten pyörä, moottori, kuljetta<strong>ja</strong>, <strong>ja</strong>lankulki<strong>ja</strong>, kuorma-auto,<br />

henkilöauto, lin<strong>ja</strong>-auto, ambulanssi, tie, öljy jne…<br />

Olio-ohjelmoinnissa luokkia käytetään reaalimaailman käsitteiden kuvaamiseen. Kysymys kuuluukin, miten<br />

käsitteiden välisiä yhteyksiä kuvataan Miten pyörä liittyy autoon Entä moottori jne <br />

Periytymisen avulla voidaan ilmaista käsitteiden välisiä hierarkkisia yhteyksiä ohjelmointikielellä.<br />

Esimerkiksi ympyrällä, suorakulmiolla <strong>ja</strong> kolmiolla on jotain yhteistä. Kaikki ovat geometrisia muoto<strong>ja</strong>, joilla on<br />

tietty pinta-ala. Kaikkien pinta-ala kuitenkin tunnetusti lasketaan eri tavalla. Ohjelmassa täytyy siten määritellä<br />

kolme luokkaa: Circle, Triangle <strong>ja</strong> Shape. Luokka Shape on kantaluokka <strong>ja</strong> Circle <strong>ja</strong> Triangle ovat kantaluokasta<br />

johdettu<strong>ja</strong> luokkia.<br />

Johdetut<br />

luokat <strong>ja</strong> public-periytymistapa<br />

1


T740103 Olio-ohjelmointi<br />

<strong>Osa</strong> 5: <strong>Periytyminen</strong> <strong>ja</strong> <strong>polymorfismi</strong><br />

© Jukka Jauhiainen OAMK <strong>Tekniikan</strong> yksikkö 2010<br />

Tarkastellaan ohjelmaa, jonka on tarkoitus ylläpitää yrityksen henkilötietojärjestelmää. Yrityksessä on<br />

kahdenlaisia työntekijöitä, tavallisia työntekijöitä <strong>ja</strong> eri tason johtajia. Luokan määritys voisi näyttää tältä:<br />

class duunari<br />

{<br />

protected:<br />

string etunimi,sukunimi;<br />

int osasto;<br />

double palkka;<br />

public:<br />

void duunaile();<br />

duunari();<br />

~duunari();<br />

};<br />

class pomo : public duunari<br />

{<br />

private:<br />

int alaistenLKM;<br />

public:<br />

void johda();<br />

pomo();<br />

~pomo();<br />

}<br />

Johta<strong>ja</strong> on välttämättä aina myös yrityksen palkkalistoilla oleva työntekijä, mutta kaikki työntekijät eivät ole<br />

johtajia. Johta<strong>ja</strong>an siis liittyy tieto<strong>ja</strong>, joita työntekijään ei liity, esimerkiksi alaisten lukumäärä. Johta<strong>ja</strong> on<br />

työntekijä, johon liittyy joitakin lisäominaisuuksia. Englanninkielessä puhutaan is-a-periytymisestä: A manager<br />

is an employee.<br />

Olio-ohjelmoinnin käsitteillä asia voidaan esittää siten, että luokka pomo on johdettu luokasta duunari. Toisin<br />

päin ilmaistuna, luokka duunari on kantaluokka luokalle pomo. Luokka pomo sisältää (perii) kaikki luokan<br />

duunari ominaisuudet <strong>ja</strong> siihen sisältyy omia ominaisuuksia, joita ei kuulu luokkaan duunari.<br />

Johdettu luokka on siis yleensä aina suurempi kuin kantaluokka. Se sisältää enemmän tieto- <strong>ja</strong> / tai<br />

aliohjelmajäseniä.<br />

Kantaluokan <strong>ja</strong> johdetun luokan ohella käytetään termejä yliluokka <strong>ja</strong> aliluokka.<br />

Public-periytymistavan yleinen muoto on:<br />

class yliluokka<br />

{<br />

private:<br />

protected:<br />

2


public:<br />

};<br />

T740103 Olio-ohjelmointi<br />

<strong>Osa</strong> 5: <strong>Periytyminen</strong> <strong>ja</strong> <strong>polymorfismi</strong><br />

© Jukka Jauhiainen OAMK <strong>Tekniikan</strong> yksikkö 2010<br />

class aliluokka : public yliluokka<br />

{<br />

private:<br />

public:<br />

};<br />

Luokan määrittelyssä esiintyy uusi avainsana protected. Sana protected määrittelee luokan suo<strong>ja</strong>tun jäsenen.<br />

Yksityisen <strong>ja</strong> suo<strong>ja</strong>tun jäsenen välinen ero on siinä, miten kyseisestä luokasta johdetut luokat pystyvät<br />

käsittelemään niiden sisältämiä tieto<strong>ja</strong>:<br />

Suo<strong>ja</strong>ttu<strong>ja</strong> jäseniä (protected) voivat käsitellä myös luokasta johdetut luokat.<br />

Yksityisiä jäseniä (private) voi käsitellä vain luokka itse, ei siitä johdetut luokat.<br />

Julkisia jäseniä (public) voi käsitellä mikä tahansa, myös luokkaan kuulumaton aliohjelma, mukaan<br />

lukien main().<br />

Edellä siis luokan duunari kaikki tietojäsenet on määriteltävä suo<strong>ja</strong>tuiksi, koska siitä johdetun luokan pomo<br />

pitää pystyä käsittelemään duunarin tieto<strong>ja</strong>. Sen si<strong>ja</strong>an luokasta pomo ei ole johdettu luokkia, eikä duunarin<br />

tarvitse tietää, montako alaista pomolla on. Siksi luokan pomo ainoa tietojäsen alaistenLKM pitää määritellä<br />

yksityiseksi.<br />

Alla olevaan taulukkoon on koottu, kenellä on oikeus käsitellä luokan jäseniä (joko tieto- tai aliohjelmajäseniä).<br />

Access<br />

public protected private<br />

Saman luokan jäsenet kyllä kyllä kyllä<br />

Johdettujen luokkien jäsenet kyllä Kyllä ei<br />

Ei-jäsenet kyllä ei ei<br />

Kanta- <strong>ja</strong> johdettu luokka käsittelevät kumpikin omia yksityisiä jäseniään. Johdetun luokan aliohjelmissa<br />

voidaan viitata kantaluokan protected- <strong>ja</strong> public-määreen jäljessä esiteltyihin jäseniin. Public-periytymistapa<br />

3


T740103 Olio-ohjelmointi<br />

<strong>Osa</strong> 5: <strong>Periytyminen</strong> <strong>ja</strong> <strong>polymorfismi</strong><br />

© Jukka Jauhiainen OAMK <strong>Tekniikan</strong> yksikkö 2010<br />

pitää kantaluokan jäsenten näkyvyyssäännöt johdetussa luokassa ennallaan. Jos johdetusta luokasta<br />

periytetään uusi johdettu luokka, näkee uusi luokka ylimmän luokan protected-jäsenet myös oman<br />

kantaluokkansa protected-jäseninä. Vastaavasti näkyvät ylimmän luokan public-jäsenet.<br />

Kanta- <strong>ja</strong> johdettu luokka voivat sisältää tarvittaessa samannimisiä tieto<strong>ja</strong> <strong>ja</strong> aliohjelmia. Tässä tapauksessa<br />

johdettu luokka peittää kantaluokan vastaavannimisen aliohjelman.<br />

Esimerkki: Monikulmio<br />

Mitä yhteistä on suorakulmiolla <strong>ja</strong> kolmiolla Molemmat ovat monikulmioita (engl. polygon) <strong>ja</strong> molempien<br />

pinta-ala lasketaan korkeuden <strong>ja</strong> leveyden avulla. Tämä yhteys voitaisiin esittää luokkahierarkiana<br />

CPolygon on kantaluokka, josta voidaan periyttää johdetut luokat CRectangle (suorakulmio) <strong>ja</strong><br />

Ctriangle (kolmio).<br />

Seuraavassa on esitetty luokan CPolygon määrittely:<br />

class CPolygon {<br />

protected:<br />

int width, height;<br />

public:<br />

void set_values (int a, int b)<br />

{ width=a; height=b;}<br />

};<br />

4


T740103 Olio-ohjelmointi<br />

<strong>Osa</strong> 5: <strong>Periytyminen</strong> <strong>ja</strong> <strong>polymorfismi</strong><br />

© Jukka Jauhiainen OAMK <strong>Tekniikan</strong> yksikkö 2010<br />

Luokka sisältää suo<strong>ja</strong>tut tietojäsenet width <strong>ja</strong> height, sekä yhden julkisen aliohjelmajäsenen<br />

set_values(). Johdetut luokat perivät kantaluokan jäsenet, joten niihin tarvitsee erikseen<br />

kirjoittaa ainoastaan metodit, jotka laskevat pinta-alan (pinta-alan laskentahan on tunnetusti erilainen<br />

kolmiolle <strong>ja</strong> suorakulmiolle):<br />

class CRectangle: public CPolygon {<br />

public:<br />

int area (void)<br />

{ return (width * height); }<br />

};<br />

class CTriangle: public CPolygon {<br />

public:<br />

int area (void)<br />

{ return (width * height / 2); }<br />

};<br />

Pääohjelmassa oliota voidaan kutsua esimerkiksi seuraavasti:<br />

int main () {<br />

CRectangle rect;<br />

CTriangle trgl;<br />

rect.set_values (4,5);<br />

trgl.set_values (4,5);<br />

cout


T740103 Olio-ohjelmointi<br />

<strong>Osa</strong> 5: <strong>Periytyminen</strong> <strong>ja</strong> <strong>polymorfismi</strong><br />

© Jukka Jauhiainen OAMK <strong>Tekniikan</strong> yksikkö 2010<br />

Public-periytymisessä johdetun luokan jäsenillä on samat suo<strong>ja</strong>usmekanismit kuin kantaluokassa. Jos jäsen on<br />

kantaluokassa tyyppiä protected, on se myös johdetussa luokassa protected.<br />

Edellä kaksoispisteen jäljessä oleva määre kertoo luokan jäsenten minimisuo<strong>ja</strong>ustason. Jos periytyminen<br />

määritellään olevan tyyppiä protected, ovat kaikki johdetun luokan jäsenet vähintään tyyppiä protected<br />

(kantaluokan public-jäsenet muuttuvat protected-tyyppisiksi). Jos periytyminen määritellään olevan tyyppiä<br />

private, ovat kaikki johdetun luokan jäsenet tyyppiä private (siis kantaluokan sekä public- että<br />

protected-jäsenet muuttuvat private-tyyppisiksi). Johdetulla luokalla voi olla omia jäseniä, jotka eivät periydy<br />

kantaluokasta, joilla on ”löyhemmät” suo<strong>ja</strong>usominaisuudet.<br />

Mitä tapahtuu, jos CRectangle määriteltäisiin periytyväksi luokasta CPolygon protected-tyyppisesti <br />

class CRectangle: protected CPolygon {<br />

public:<br />

int area (void)<br />

{ return (width * height); }<br />

};<br />

CPolygon sisältää julkisen aliohjelmajäsenen set_values(). Määrityksen jälkeen se muuttuisi<br />

protected-tyyppiseksi. Onko tällä muutoksella vaikutuksia ohjelman toimintaan <br />

Vastaavasti määrittely:<br />

class CRectangle: private CPolygon {<br />

public:<br />

};<br />

int area (void)<br />

{ return (width * height); }<br />

muuttaisi set_values()-metodin private-tyyppiseksi. Entä onko tällä vaikutusta ohjelman toimintaan <br />

Kummassakaan edellä mainitussa tapauksessa set_values()-metodia ei enää voi kutsua pääohjelmasta,<br />

koska main() ei ole luokan CPolygon jäsen eikä aliluokka. Käytännössä public-periytyminen on<br />

ylivoimaisesti yleisin.<br />

6


T740103 Olio-ohjelmointi<br />

<strong>Osa</strong> 5: <strong>Periytyminen</strong> <strong>ja</strong> <strong>polymorfismi</strong><br />

© Jukka Jauhiainen OAMK <strong>Tekniikan</strong> yksikkö 2010<br />

13. Polymorfismi<br />

Eräs johdetun luokan keskeinen ominaisuus on, että osoitin johdettuun luokkaan on tyyppiyhteensopiva<br />

kantaluokkaan osoittavan osoittimen kanssa. Seuraavassa ohjelmassa on luotu kaksi osoitinta luokkaan<br />

CPolygon (ppoly1 <strong>ja</strong> ppoly2) <strong>ja</strong> asetetaan ne osoittamaan luokan olioihin rect <strong>ja</strong> trgl. Tämä on<br />

mahdollista, koska sekä luokat CRectangle että CTriangle on johdettu CPolygon-luokasta. Ainoa<br />

rajoitus on, että koska ppoly1 <strong>ja</strong> ppoly2 ovat tyyppiä CPolygon, niillä on käytössä vain ne palvelut, jotka<br />

CPolygon tarjoaa, ei johdettujen luokkien palvelut. Esimerkiksi siis pinta-alan laskenta ei ole näiden<br />

osoittimien avulla mahdollista, koska ne määritellään johdetuissa luokissa.<br />

Jos pinta-ala haluttaisiin laskea luokkaan CPolygon osoittavien osoittimien avulla, pitäisi laskenta olla<br />

määritelty tässä luokassa. Ongelmaksi muodostuu, että pinta-ala on erilainen kolmiolla <strong>ja</strong> suorakulmiolle.<br />

Ongelma on mahdollista ratkaista virtuaalisten jäsenten avulla.<br />

// pointers to base class<br />

#include <br />

using namespace std;<br />

class CPolygon {<br />

protected:<br />

int width, height;<br />

public:<br />

void set_values (int a, int b)<br />

{ width=a; height=b; }<br />

};<br />

class CRectangle: public CPolygon {<br />

public:<br />

int area ()<br />

{ return (width * height); }<br />

};<br />

class CTriangle: public CPolygon {<br />

public:<br />

int area ()<br />

{ return (width * height / 2); }<br />

};<br />

int main () {<br />

CRectangle rect;<br />

CTriangle trgl;<br />

CPolygon * ppoly1 = &rect;<br />

CPolygon * ppoly2 = &trgl;<br />

ppoly1->set_values (4,5);<br />

ppoly2->set_values (4,5);<br />

7


T740103 Olio-ohjelmointi<br />

<strong>Osa</strong> 5: <strong>Periytyminen</strong> <strong>ja</strong> <strong>polymorfismi</strong><br />

© Jukka Jauhiainen OAMK <strong>Tekniikan</strong> yksikkö 2010<br />

}<br />

cout


T740103 Olio-ohjelmointi<br />

<strong>Osa</strong> 5: <strong>Periytyminen</strong> <strong>ja</strong> <strong>polymorfismi</strong><br />

© Jukka Jauhiainen OAMK <strong>Tekniikan</strong> yksikkö 2010<br />

}<br />

cout area()


T740103 Olio-ohjelmointi<br />

<strong>Osa</strong> 5: <strong>Periytyminen</strong> <strong>ja</strong> <strong>polymorfismi</strong><br />

© Jukka Jauhiainen OAMK <strong>Tekniikan</strong> yksikkö 2010<br />

On virheellinen jos CPolygon on abstrakti kantaluokka, mutta osoittimet<br />

CPolygon * ppoly1;<br />

CPolygon * ppoly2;<br />

ovat OK. Näillä osoittimilla voidaan osoittaa johdettujen luokkien olioihin.<br />

// abstract base class<br />

#include <br />

using namespace std;<br />

class CPolygon {<br />

protected:<br />

int width, height;<br />

public:<br />

void set_values (int a, int b)<br />

{ width=a; height=b; }<br />

virtual int area (void) =0;<br />

};<br />

class CRectangle: public CPolygon {<br />

public:<br />

int area (void)<br />

{ return (width * height); }<br />

};<br />

class CTriangle: public CPolygon {<br />

public:<br />

int area (void)<br />

{ return (width * height / 2); }<br />

};<br />

int main () {<br />

CRectangle rect;<br />

CTriangle trgl;<br />

CPolygon *ppoly1 = &rect;<br />

CPolygon *ppoly2 = &trgl;<br />

ppoly1->set_values (4,5);<br />

ppoly2->set_values (4,5);<br />

cout area()


T740103 Olio-ohjelmointi<br />

<strong>Osa</strong> 5: <strong>Periytyminen</strong> <strong>ja</strong> <strong>polymorfismi</strong><br />

© Jukka Jauhiainen OAMK <strong>Tekniikan</strong> yksikkö 2010<br />

jäsenfunktio luokkaan CPolygon, joka tulostaa näytölle olion pinta-alan, vaikka luokassa itsessään ei<br />

ole pinta-alan laskentaa:<br />

// pure virtual members can be called<br />

// from the abstract base class<br />

#include <br />

using namespace std;<br />

class CPolygon {<br />

protected:<br />

int width, height;<br />

public:<br />

void set_values (int a, int b)<br />

{ width=a; height=b; }<br />

virtual int area (void) =0;<br />

void printarea (void)<br />

{ cout area() set_values (4,5);<br />

ppoly2->set_values (4,5);<br />

ppoly1->printarea();<br />

ppoly2->printarea();<br />

return 0;<br />

}<br />

Viimeisessä esimerkissä olioille varataan muistia dynaamisesti:<br />

// dynamic allocation and polymorphism<br />

#include <br />

using namespace std;<br />

class CPolygon {<br />

11


T740103 Olio-ohjelmointi<br />

<strong>Osa</strong> 5: <strong>Periytyminen</strong> <strong>ja</strong> <strong>polymorfismi</strong><br />

© Jukka Jauhiainen OAMK <strong>Tekniikan</strong> yksikkö 2010<br />

protected:<br />

int width, height;<br />

public:<br />

void set_values (int a, int b)<br />

{ width=a; height=b; }<br />

virtual int area (void) =0;<br />

void printarea (void)<br />

{ cout area() set_values (4,5);<br />

ppoly2->set_values (4,5);<br />

ppoly1->printarea();<br />

ppoly2->printarea();<br />

delete ppoly1;<br />

delete ppoly2;<br />

return 0;<br />

}<br />

Huomaa, että vaikka osoittimien ppoly1 <strong>ja</strong> ppoly2 tyyppi on CPolygon, mutta kun niille allokoidaan<br />

muistia new-metodilla, ovat tyypit CRectangle <strong>ja</strong> CTriangle. Tällöin siis käytännössä ppoly1 on tyyppiä<br />

CRectangle <strong>ja</strong> ppoly2 tyyppiä CTriangle.<br />

12

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!