Osa 5: Periytyminen ja polymorfismi - Tekniikan yksikkö - Oamk
Osa 5: Periytyminen ja polymorfismi - Tekniikan yksikkö - Oamk
Osa 5: Periytyminen ja polymorfismi - Tekniikan yksikkö - Oamk
Transform your PDFs into Flipbooks and boost your revenue!
Leverage SEO-optimized Flipbooks, powerful backlinks, and multimedia content to professionally showcase your products and significantly increase your reach.
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 = ▭<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 = ▭<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