olio-ohjelmoinnin perusteet luento 4: perinnästä

91
Olio-ohjelmoinnin perusteet luento 4: Perinnästä Jani Rönkkönen LTY/Tietotekniikan osasto Kalvot on muokattu Sami Jantusen luentokalvoista viime vuodelta.

Upload: adah

Post on 20-Jan-2016

45 views

Category:

Documents


0 download

DESCRIPTION

Olio-ohjelmoinnin perusteet luento 4: Perinnästä. Jani Rönkkönen LTY/Tietotekniikan osasto Kalvot on muokattu Sami Jantusen luentokalvoista viime vuodelta. Sisältö. Johdanto Kertausta Esimerkki Yhteenveto Luokkien näkyvyysmääreet Perintä Toiminnan korvaaminen Moniperintä Yhteenveto. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Olio-ohjelmoinnin perusteetluento 4: Perinnästä

Jani RönkkönenLTY/Tietotekniikan osasto

Kalvot on muokattu Sami Jantusen luentokalvoista viime vuodelta.

Page 2: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Sisältö Johdanto

Kertausta Esimerkki Yhteenveto

Luokkien näkyvyysmääreet Perintä Toiminnan korvaaminen Moniperintä Yhteenveto

Page 3: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

PerintäMuistatko vielä….?

Perintä tarkoittaa periaatetta siitä, että yleisempi määrittely on myös voimassa erikoistuneissa olioissa

Sanomme, että kukkakauppias perii myös kauppiaan ja ihmisen toiminnallisuuden

Ihminen

Kauppias

Kukkakauppias

Page 4: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

PerintäMuistatko vielä….?

Perinnän idea: Luokat voidaan organisoida

hierarkkisiin perintäpuihin Lapsiluokka perii vanhempiensa

tiedon ja toiminnallisuuden Abstrakti isäluokka on sellainen,

mistä ei voidan tehdä omaa oliota, mutta mitä käytetään lapsiluokkien määrittelyssä

Page 5: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

PerintäMuistatko vielä….?

Page 6: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

PerintäMuistatko vielä….?

Eläin

...Niveljalkainen Selkäjänteinen

Hämähäkkieläin Hyönteinen Matelija Nisäkäs Lintu

...

......

... ......

Leppäkerttu Kissa Ihminen

...

Page 7: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

LuokkahierarkiaMammal

Land-Mammal

int weight

int numLegs

Dogboolean rabid

Chihuahua

giveBirth( )

SheepDog

Kuinka monta attribuuttia koiralla on?

...

Page 8: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Huomaathan että:

Land-Mammal on Dog- luokan isäluokka (superclass), mutta Mammal –luokan lapsiluokka (subclass)

Dog –luokalla on kolme attribuuttiaweight, numLegs ja rabidkaksi attribuuttia perinnän kautta ja yksi omassa luokassa

Page 9: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Muistatko vielä? Koirat eläintarhassa CDog.h

Vielä paljon kerrottavaa Ei vielä ihan tyylipuhdas luokan esittely.

CDogbool rabidOrNot

int weight

string name

void growl()void eat()

#ifndef CDog_H #define CDog_H#include <iostream.h>class CDog {

int weight;bool rabidOrNot;std:string name;

public:CDog (int x, std::string y);

~CDog(); //tuhoajan esittelybool getRabid ( )const;void setRabid (bool x);std::string getName ( )const;void setName (std::string z);int getWeight ( )const;void setWeight (int x);void eat( );void growl( )const;

}; #endif /* CDog_H */

Page 10: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Muistatko vielä? Koirat eläintarhassa CDog.cpp

#include <string.h>#include “CDog.h”

using namespace std;

// ConstructorCDog::CDog (int x, string y) {

rabidOrNot = false;weight = x;name = y;

}// destructorCDog::~CDog(){}void CDog::eat ( ) {

cout << name << “ is eating”<< endl;weight++;}void CDog::growl ( ) const{

cout << “Grrrr”;}

bool CDog::getRabid ( ) const{ return rabidOrNot;}void CDog::setRabid (bool x) { rabidOrNot = x;}int CDog::getWeight ( ) const{ return weight;}void CDog::setWeight (int y) { weight = y;}string CDog::getName ( ) const{ return name;}void setName (string z) { name = z;}

Page 11: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Ongelmia!

Mikä tahansa koira ei kelpaa!Puudeli se olla

pitää!

Miten saamme luotua puudelin siten, ettei tarvitsisi kirjoittaa paljon koodia uusiksi??

Page 12: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Perintä:Puudeli on koira

Puudeli on koira (muistathan “is-a” testin)

Käytetään hyväksi CDog-luokan toteutus perimällä siitä CPoodle-luokka

CDogbool rabidOrNot

int weight

string name

void growl()void eat()

CPoodle

Page 13: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

No niin…. Ryhdytään hommiin!Luodaan puudeli-luokka (sekä .h, että .cpp tiedostot)

#include "CDog.h“ CPoodle.h

class CPoodle:public CDog {public:CPoodle(int x, std::string y);

};

Tässä suoritetaan perintä CDog -luokasta

#include <iostream>

#include "CPoodle.h"

using namespace std;

CPoodle::CPoodle(int x, string y) : CDog (x,y){

cout << “Tuli muuten tehtyä puudeli" << endl;}

CPoodle.cpp

Page 14: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Mitäs täällä tapahtuu?

Isäluokan rakentajaa kutsutaan aina!*

*Huomaa!: Jos isäluokan rakentajaa ei kutsuta eksplisiittisesti itse, kääntäjä yrittää kutsua automaattisesti isäluokan oletusrakentajaa (mitä ei tässä esimerkissä ole olemassa)

CPoodle.cpp

CPoodle::CPoodle(int x, string y) : CDog (x,y)

{

cout << “Tuli muuten tehtyä puudeli" << endl;

}

Normaalia rakentaja tavaraa

Page 15: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Mitä tuli taas tehtyä??

Loimme puudeliluokan jolla on kaikki attribuutit ja metodit kun CDog-luokallakin

Page 16: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Ongelmia!

Puudelit ei sano “Grrrrrrr”! Eihän??? Ne sanoo “Yip”!

void CDog::growl ( ) {cout << “Grrrr”;

}

Page 17: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Muistatko vielä?Toiminnallisuuden korvaaminen

Eläin

Nisäkäs Lintu

Selkäjänteinen

Nisäkkäät synnyttävät eläviä poikasia

Linnut munivat munia The Australian Platypus on

nisäkäs, mutta luo munia

Page 18: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Muistatko vielä?Toiminnallisuuden korvaaminen

On mahdollista korvata (override) isäluokassa määritelty toiminnallisuus toteuttamalla lapsiluokkaan saman niminen toiminnallisuus

Sopivan metodin etsintä aloitetaan aina lapsiluokasta. Jos lapsiluokassa ei ole toteutettuna haluttua toiminnallisuutta, siirrytään etsimään sitä isäluokasta

Page 19: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Tehdäänpä jotain puudelin murinalle!!!

Korvataan CDog –luokan growl -metodi

Yksinkertaista, kirjoitetaan Puudeliluokkaan vain samanniminen metodi

GRRRRR

Page 20: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Kiltti puudeli!

YIP

CPoodle.cpp

CPoodle::CPoodle(int x, string y) : CDog (x,y){

cout << “Tuli muuten tehtyä puudeli" << endl;

}

void CPoodle::growl( ) const{

cout << "Yip!" << endl;

}

#include "CDog.h“ CPoodle.h

class CPoodle:public CDog {

public:

CPoodle(int x, std::string y);

void growl() const;

};

Page 21: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Mitä juuri opimme?Perinnän määrittely

#include "CDog.h“ CPoodle.h

class CPoodle:public CDog {public:CPoodle(int x, std::string y);

};

Tässä suoritetaan perintä CDog -luokasta

Puhumme perinnästä hetken kuluttua lisää!

Page 22: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Mitä juuri opimme?Rakentajien käyttö perinnän yhteydessä

Isäluokan rakentajaa kutsutaan aina!*

CPoodle.cpp

CPoodle::CPoodle(int x, string y):CDog(x,y)

{

cout << “Tuli muuten tehtyä puudeli" << endl;

}

Normaalia rakentaja tavaraa

Luokkia perittäessä on rakentajien ja purkajien käytössä on paljon huomioitavaa Puhumme tästä hetken kuluttua lisää!

Page 23: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Mitä juuri opimme?Toiminnan korvaaminen

GRRRRYIPToiminnan korvaaminen on oleellinen osa perintää.

Puhumme tästä hetken kuluttua lisää!

Page 24: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Missä mennään Johdanto

Kertausta Esimerkki Yhteenveto

Luokkien näkyvyysmääreet Perintä Toiminnan korvaaminen Moniperintä Yhteenveto

Page 25: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Luokan näkyvyysmääreetclass CPoodle{public: //Tänne tulevat asiat näkyvät //luokasta ulosprotected: //Tänne tulevat asiat näkyvät //vain aliluokilleprivate: //Tänne tulevat asiat ei näy // ulospäin};

Page 26: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Sääntöjä

Oletusarvoinen näkyvyysmääre on private

Saatat nähdä koodia, missä jäsenmuuttujat on määritelty luokassa ensimmäisenä ilman näkyvyysmäärettä (=private:). Huonoa tyyliä! Selkeämpää kirjoittaa luokka public:,

protected:, private: -järjestyksessä

Page 27: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Public:

Julkinen rajapinta. Kaikki voi käyttää Luokka: Koira

•murise•syö•kerroPaino•kerroNimi•oletkoVesikauhuinen

Page 28: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Public: Ohjeita

Ole huolellinen julkisen rajapinnan suunnittelussa!

Rajapinta on lupaus luokan tarjoamista palveluista.

Rajapintaan kuuluvien asioiden tulisi säilyä muuttumattomina

Rajapinnan tulisi olla “minimaalinen, mutta täydellinen”

Ei ylimääräistä tavaraa “varmuuden vuoksi” Jos jotain puuttuu, niin luokan käyttäjällä ei mitään

mahdollisuuksia korjata puutettaMinun luokka tekee tätä eikä mitään muuta

Page 29: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Public: Ohjeita

Jäsenmuuttujat on syytä pitää visusti piilossa!

Tiedon piilottaminen on yksi olioajattelun perusajatuksista

Voi tulla tarve siirtää jäsenmuuttuja muualle tai korvata se jollain toisella rakenteella Et voi tehdä tätä jos olet jo julkaissut muuttujasi

On parempi, että oliolla on täysi kontrolli tietoonsa Jos muuttuja on julkinen, olio ei voi mitenkään

tietää milloin arvoa luetaan tai sitä muutetaanEt pääse “kopeloimaan” tietojani!

Page 30: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Protected:

Käytetään perittäessä luokkia Muulloin toimii samoin

kuin private: Sallii lapsiluokkien

käyttää jäseniään

Page 31: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Private: Kaikkein rajoittavin näkyvyysmääre Vain luokka itse voi käyttää private

jäseniä (myös ystäväfunktiot ja –luokat, mutta tästä lisää myöhemmin)

samantyyppinen olio pääsee myös käsiksi toisen olion privaatteihin tietoihin

Julista privaatiksi: jäsenmuuttujat apufunktiot

Page 32: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Missä mennään Johdanto

Kertausta Esimerkki Yhteenveto

Luokkien näkyvyysmääreet Perintä Toiminnan korvaaminen Moniperintä Yhteenveto

Page 33: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Perinnän määrittely#include "CDog.h“ CPoodle.h

class CPoodle:public CDog {public:CPoodle(int x, string y);

};

Tässä suoritetaan perintä CDog -luokasta

Page 34: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Perinnän määrittely class CPoodle:public CDog

C++:ssa on oletuksena yksityinen perintä class B : A {…} tarkoittaa samaa kuin class B : private A {…}

Tämä on hieman outoa, sillä julkinen (public:) on kuitenkin yleisintä

Page 35: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

PerintätavoistaEsimerkki (Hannu Peltosen kirjasta Johdatus C++

ohjelmointikieleen)

Haluamme rakentaa yleiskäyttöisen luokan, jota apuna käyttäen voimme toteuttaa erilaisia 2-suuntaisia listojanextprevdata

nextprevdata

nextprevdata

Page 36: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

2-suuntainen lista (Deque)

Ensimmäiseksi määrittelemme Deque luokan jonka alkioihin voi tallentaa mitä tahansa tietoa

Luokkaa ei käytetä suoraan, vaan siitä johdetaan uusia luokkia erityyppisten alkioiden tallentamista varten.

nextprevdata

nextprevdata

nextprevdata

Page 37: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

2-suuntainen lista (Deque)Määritellään ensin jäsenmuuttujat

class Deque{

…private:

struct Item{

Item *prev;Item *next;void *data;

};Item *first;Item *last;Item *curr;

};

nextprevdata

nextprevdata

nextprevdata

Page 38: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

2-suuntainen lista (Deque)Määritellään julkinen rajapinta

void Deque::goBeforeFirst(){

curr = first;}void Deque:: goAfterLast(){

curr = last;}void Deque:: forth(){

if (curr != last)curr = curr->next;

}void Deque:: back(){

if (curr != first)curr = curr->prev;

}int Deque:: isBeforeFirst() const{

return curr == first;}int Deque:: isAfterLast() const{

return curr == last;}next

prevdata

nextprevdata

nextprevdata

class Deque{public:

void goBeforeFirst();void goAfterLast();void forth();void back();int isBeforeFirst() const;int isAfterLast() const;...

};

Page 39: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

2-suuntainen lista (Deque)Rakentaja

nextprevdata

nextprevdata

nextprevdata

Deque oli tarkoitettu yleiskäyttöiseksi luokaksi, joka ei voi esiintyä yksinään.

Miten voidaan varmistua siitä, että ohjelmassa ei pysty määrittelemään suoraan tyyppiä Deque olevia muuttujia?

Page 40: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

2-suuntainen lista (Deque)Rakentaja

Deque::Deque(){

first = new Item; last = new Item; first->prev = NULL;first->next = last; last->prev = first;last->next = NULL;curr = first;

};

nextprevdata

nextprevdata

nextprevdata

class Deque{...

protected:Deque();...

};

Kun rakentaja on protected, ei sitä voi kutsua muualta kuin periytetystä luokasta

Page 41: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

2-suuntainen lista (Deque)

Valmista?

Puuttuuko vielä jotain?

Page 42: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

2-suuntainen lista (Deque)Alkion lisäys

class Deque{

...protected:

void insertBeforeCurrent(void*);void insertAfterCurrent(void*);

...};

void Deque::insertBeforeCurrent(void *p){

if (curr != first){

Item *newItem = new Item;newItem->data = p;newItem->next = curr;newItem->prev = curr->prev;curr->prev->next = newItem;curr->prev = newItem;curr = newItem;

}}void Deque::insertAfterCurrent(void *p){

if (curr != last){

forth();insertBeforeCurrent (p);

}}

nextprevdata

nextprevdata

nextprevdata

Page 43: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

2-suuntainen lista (Deque)Alkion poisto

class Deque{

...protected:

void *removeCurrentAndGoBack();void *removeCurrentAndGoForth();

private:void *remove(Item *newCurr);...

};

void * Deque::removeCurrentAndGoBack(){

return remove(curr->prev);}

void * Deque::removeCurrentAndGoForth(){

return remove(curr->next);}

void * Deque::remove (Item *newCurr){

if (curr == first || curr == last )return NULL;

else{

void *res = curr->data;curr->prev->next = curr->next;curr->next->prev = curr->prev;delete curr;curr = newCurr;return res;

}}

nextprevdata

nextprevdata

nextprevdata

Page 44: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

2-suuntainen lista (Deque)Nykyisen alkion saanti ja listan tuhoaminen

class Deque{

...protected:

void *current () const;~Deque(); ...

};

void * Deque::current() const{

return (curr == first || curr == last) ?NULL : curr->data;

}

Deque:: ~Deque (){

Item *p, *next;for (p = first; p != NULL; p = next){

next = p->next;delete p;

}}

nextprevdata

nextprevdata

nextprevdata

Page 45: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Hurraa!!!

VIHDOINKIN VALMISTA!

Page 46: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Mitä tuli tehtyä?

Loimme luokan joka on elegantti ja yleiskäyttöinen Ei käytetä yksin vaan tästä

johdetaan helposti erilaisia listaratkaisuja minimaallisella työllä

Koodin uudelleenkäyttö! Hyvä esimerkki

oliopohjaisuudesta ja perinnästä!

Page 47: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Listoja liukuhihnalta!(IntDeque)Otetaanpa Deque luokka hyötykäyttöön! Luodaan Int-pohjainen lista

class IntDeque: public Deque{public:

IntDeque();void insert (int);void remove();int current () const; ~IntDeque();

};

nextprevdata

nextprevdata

nextprevdata

IntDeque

void goBeforeFirst();void goAfterLast();void forth();void back();int isBeforeFirst() const;int isAfterLast() const;IntDeque();void insert (int);void remove();int current () const; ~IntDeque();

Deque-luokasta peritty

Uusi toiminnallisuus

Page 48: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

IntDeque toteutus

IntDeque::IntDeque(){ //kutsutaan isäluokan }//oletusmuodostinta automaattisesti

void IntDeque::insert (int n){

int *ptr = new int;*ptr = n;insertAfterCurrent (ptr);

}

void IntDeque::remove (){

delete (int*) removeCurrentAndGoBack();}

int IntDeque::current() const{

int *ptr = (int*)Deque::current();return (ptr != NULL )? *ptr : -1;

}

IntDeque:: ~IntDeque(){

goBeforeFirst();forth();while (!isAfterLast()){

delete Deque::current();forth();

}}

Page 49: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

IntDequeMitä opimme? Koodin uudelleenkäyttö on

helppoa ja mukavaa! Kun perit public: -määreellä perit

isäluokan rajapinnan ja saat sen public- ja protected –jäsenet käyttöösi

Page 50: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Listoja liukuhihnaltaIntStack

Seuraavaksi haluamme tehdä pinon

Jotain pielessä! Mitä?IntDeque

void goBeforeFirst();void goAfterLast();void forth();void back();int isBeforeFirst() const;int isAfterLast() const;IntStack();int empty () const; void push (int);int top () const; int pop (); ~IntStack();

Deque-luokasta peritty

Uusi toiminnallisuus

Page 51: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

IntStackjulkinen perintä

IntDeque

void goBeforeFirst();void goAfterLast();void forth();void back();int isBeforeFirst() const;int isAfterLast() const;IntStack();int empty () const; void push (int);int top () const; void pop (); ~IntStack();

Deque-luokasta peritty

Uusi toiminnallisuus

Ku emmie haluu noin paljon tavaraa miun

julkiseen rajapintaan!!!!!

Mitäs nyt? Haluamme vain käyttää hyväksi olemassa olevaa

toiminnallisuutta. Emme halua periä isäluokan julkista rajapintaa

Page 52: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

IntStack- yksityinen perintä class IntStack : private IntDeque

Perittiin toiminnallisuus, mutta ei rajapintaa Yksityisesti peritty luokka voi käyttää

kantaluokan suojattuja jäseniä aivan kuin julkisestikin johdettu luokka

Luokkien ulkopuolella IntStack:lla ja IntDequella ei näytä olevan mitään tekemistä keskenään.

Mitä saimme aikaan yksityisellä perinnällä?

Page 53: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

protected perintä class IntStack : protected IntDeque

Yhteneväisyydet: Molemmat sallivat kantaluokan toiminnan korvaamisen Kumpikaan ei tunnusta sukulaisuuttaan isäluokkaan

Erot: protected perintä sallii lastenlasten tietävän perintäsuhteesta.

protected johtamastasi luokasta perityt luokat näkevät sisältösi.

protected perinnän hyöty: sallii lapsiluokkiesi käyttävän hyväksi isäluokkasi

toiminnallisuutta protected perinnän haitta:

protected perinnän muuttaminen saattaa hajoittaa jotain lapsiluokissasi.

Kuinka protected perintä eroaa private perinnästä?

Page 54: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

private- ja protected perintää liittyvät näkyvyyssäännöt

Tarkastellaan seuraavia esimerkkejä:   class B                 { /*...*/ }; class D_priv : private   B { /*...*/ }; class D_prot : protected B { /*...*/ }; class D_publ : public    B { /*...*/ }; class UserClass  { B b; /*...*/ };

Mikään perityistä luokista ei pääse B-luokan yksityisiin jäseniin

B-luokan public- ja protected jäsenet ovat: D_priv- luokassa private D_prot –luokassa protected, näkyvät D_publ –luokassa samalla lailla kuin B-luokassakin

UserClass-luokka näkee vain B:n julkiset jäsenet

Page 55: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Jäsenkohtainen näkyvyyksien määrittely

Tarkastellaan edelleen seuraavaa esimerkkiä: class B                 { /*...*/ }; class D_priv : private   B { /*...*/ }; class D_prot : protected B { /*...*/ }; class D_publ : public    B { /*...*/ }; class UserClass  { B b; /*...*/ };

On mahdollista palauttaa private tai protected perinnässä muuttuneet jäsenten näkyvyydet. Suojausta ei voi muuttaa vapaammaksi tai tiukemmaksi kuin mikä se on kantaluokassa. Esimerkki:

Halutaan tietty B:n public jäsen näkyvän julkisena myös D_priv tai D_prot –luokassa.

     class D_prot : protected B {    public:       B::f;      };

Page 56: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Takaisin IntStack:n pariin Päätimme siis käyttää privaattia perintää. Uudelleenkäyttö on taas nopeaa ja helppoa. Ei

paljon mitään kirjoitettavaa:

IntStack::IntStack(){ //isäluokan rakentaja riittää}

int IntStack::empty() const{

return isBeforeFirst();}

void IntStack::push(int n) {

insert(n);}

int IntStack::top() const{

return current();}

void IntStack ::pop(){

remove();}

IntStack :: ~ IntStack(){ //isäluokan purkaja riittää}

Page 57: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

On toinenkin tapa…

Usein yksityisen perinnän kaltainen lopputulos saadaan aikaan liittämällä kantaluokan olio toisen luokan jäsenmuuttujaksi.

Deque

IntStack

<<private>> IntDeque

IntStack

Deque

Page 58: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Muistatko vielä? AggregaatioAggregaatiot ovat erikoistuneita assosiaatioita kuvaamaan luokan koostumista muista luokista

Kokonaisuutta kuvaavaa puolta kutsutaan aggregaatiksi (aggregate)

Aggregaatio kuvataan assosiaation päässä olevalla timantilla.

****

****** Region

VehiclePart

Country

Vehicle

Page 59: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

IntStack aggregaatiota hyväksi käyttäen

class IntStack

{

public:

IntStack();

int empty () const;

void push (int);

int top () const;

void pop ();

~IntStack();

private:

IntDeque q;

};

IntStack::IntStack(){ //q alustetaan IntDequen rakentajassa}

int IntStack::empty() const{

return q.isBeforeFirst();}

void IntStack::push(int n) {

q.insert(n);}int IntStack::top() const{

return q.current();}

void IntStack ::pop(){

q.remove();}

IntStack :: ~ IntStack(){ //isäluokan purkaja riittää}

Page 60: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

IntStack (private perintä) vs. IntStack (aggregaatio)

Yhteistä: Molemmat kuvaavat (has-a) koostetta. Kummassakin tapauksessa yhteys Deque-luokkaan on piilotettu

Eroja: Kooste voi sisältää useita olioita Yksityisessä perinnässä johdettu luokka voi käyttää

kantaluokan suojattuja jäseniä Kumpaa tapaa kannattaa käyttää?

Käytä aggregaatiota aina kun pystyt Käytä yksityistä perintää kun on pakko Tyypillisesti et halua päästä käsiksi muiden luokkien

sisälmyksiin. Yksityinen perintä antaisi sinulle tällaista ylimääräistä voimaa (ja vastuuta)

Yksityinen perintä on rakaampaa ylläpitää, sillä silloin on suurempi vaara, että joku muuttaa koodia siten, että se menee rikki

Page 61: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Rakentajien käyttö perinnän yhteydessä

Isäluokan rakentajaa kutsutaan aina!*

CPoodle.cpp

CPoodle::CPoodle(int x, string y) : CDog (x,y)

{

cout << “Tuli muuten tehtyä puudeli" << endl;

}

Normaalia rakentaja tavaraa

PUHUTAAN TÄSTÄ LISÄÄ SEURAAVALLA LUENNOLLA!

Page 62: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Missä mennään Johdanto

Kertausta Esimerkki Yhteenveto

Luokkien näkyvyysmääreet Perintä Toiminnan korvaaminen Moniperintä Yhteenveto

Page 63: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Toiminnan korvaaminen

GRRRRYIP Joskus aliluokan olion on tarpeen

suorittaa kantaluokasta perimänsä palvelu hieman kantaluokasta poikkeavalla tavalla

Aliluokka haluaa siis periä rajapinnan, mutta ei toteutusta

C++ tarjoaa mahdollisuuden uudelleenmääritellä (override) kantaluokasta poikkeava toiminto. Määrittelet kyseisen funktion kantaluokassa vain virtuaaliseksi (avainsanalla virtual)

Page 64: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

GRRR? YIP? VIRTUAL?? Hetkinen, eihän esimerkissä

määritelty growl –funktiota virtuaaliseksi!

Ei niin, esimerkissä oli itse asiassa kyseessä funktion peittäminen

Funktion peittäminen tapahtuu kirjoittamalla lapsiluokkaan täsmälleen saman niminen funktio

Funktion peittämisen yhteydessä lapsiluokka peittää kaikki samannimiset kantaluokan funktiot

Toiminnon peittäminen ja korvaaminen käyttäytyvät eri tavalla riippuen kutsutavasta.

Page 65: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Toiminnan peittäminen

Mitä tapahtuu?CPoodle *myPoodle;

myPoodle = new CPoodle();

CPoodle->growl();

((CDog*)myPoodle)->growl();

CDogbool rabidOrNot

int weight

string name

void growl()void eat()

CPoodlevoid growl() Vastaus:

YIPGRRRRRRRR

Page 66: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Toiminnan korvaaminen

Mitä tapahtuu?CPoodle *myPoodle;

myPoodle = new CPoodle();

CPoodle->growl();

((CDog*)myPoodle)->growl();

CDogbool rabidOrNot

int weight

string name

virtual void growl()void eat()

CPoodlevoid growl() Vastaus:

YIPYIP

Page 67: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Toiminnan korvaamisesta

Kutsutilanteesta riippuva jäsenfunkiton valinta ei ole toivottavaa

Tällainen tapahtuu helposti vahingossa silloin, kun kantaluokan jäsenfunktion esittelystä unohtuu avainsana virtual Silloin ei auta vaikka lapsiluokassa virtual

löytyisikin

Page 68: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Noniin… takaisin toiminnan korvaamisen pariin. Toiminta määritellään korvattavaksi siis virtual

avainsanaa käyttäen. Tämän jälkeen kantaluokasta periytettävillä aliluokilla on kaksi mahdollisuutta:

Hyväksyä kantaluokan tarjoama jäsenfunktion toteutus. Tällöin aliluokan ei tarvitse tehdä mitään

kantaluokan toteutus periytyy automaattisesti myös aliluokkaan

Kijoitaa oma toteutuksensa perimälleen jäsenfunktiolle. Tässä tapauksessa aliluokan esittelyssä esitellään jäsenfunktio uudelleen, ja sen jälkeen aliluokan toteutuksessa kirjoitetaan jäsenfunktiolle uusi toteutus aivan kuin normaalille aliluokan jäsenfunktiolle. Aliluokan esittelyssä avainsanan virtual toistaminen ei ole pakollista, mutta kylläkin hyvän ohjelmointityylin mukaista

Page 69: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Toiminnan korvaamisestaHuomioitavaa

On tärkeää, että korvaava funktio tarjoaa kantaluokan kannalta saman palvelun kuin alkuperäinenkin funktio.

Aliluokka voi muuttaa vain toteutusta, ei rajapintaa.

Page 70: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Päivitetään CDog.h:

Jäsenmuuttujille oma private

lohko

Growl funktiosta virtuaalinen

CDogbool rabidOrNot

int weight

string name

virtual void growl()void eat()

CPoodlevoid growl()

#ifndef CDog_H #define CDog_H#include <iostream.h>class CDog {public:

CDog (int x, std::string y); ~CDog(); //tuhoajan esittely

bool getRabid ( )const;void setRabid (bool x);std::string getName ( )const;void setName (std::string z);int getWeight ( )const;void setWeight (int x);void eat( );virtual void growl( )const;

private: int weight;

bool rabidOrNot;std:string name;

}; #endif /* CDog_H */

Page 71: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Ei muutoksia CDog.cpp:

#include <string.h>#include “CDog.h”

using namespace std;

// ConstructorCDog::CDog (int x, string y) {

rabidOrNot = false;weight = x;name = y;

}// destructorCDog::~CDog(){}void CDog::eat ( ) {

cout << name << “ is eating”<< endl;weight++;}void CDog::growl ( ) const{

cout << “Grrrr”;}

bool CDog::getRabid ( ) const{ return rabidOrNot;}void CDog::setRabid (bool x) { rabidOrNot = x;}int CDog::getWeight ( ) const{ return weight;}void CDog::setWeight (int y) { weight = y;}string CDog::getName ( ) const{ return name;}void setName (string z) { name = z;}

Page 72: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Myös puudeliin virtuallinen growl metodi:

YIP

CPoodle.cpp#include <iostream>

#include "CPoodle.h"

using namespace std;

CPoodle::CPoodle(int x, string y) : CDog (x,y){}

void CPoodle::growl( ) const{

cout << "Yip!" << endl;}

#include "CDog.h“ CPoodle.h

class CPoodle:public CDog {

public:

CPoodle(int x, std::string y);

virtual void growl() const;

};

Page 73: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Ja taas esimerkkiKoirat Kennelissä: Zoo.cpp

void main(){

CDog *kennel[5];CDog *valittuTyyppi;int valinta;

for (int i=0; i<5; i++){ cout<<“(1)Koira (2)Puudeli”; cin >> valinta; switch (valinta) { case 1: valittuTyyppi=new CDog(1,”koira”); break; case 2: valittuTyyppi=new CPoodle(1,”puudeli”); break; }

kennel[i]=valittuTyyppi;}for (i=0; i<5; i++)

kennel[i]->growl();}

GRRRR

Mitä tämä ohjelma tekee?

YIP

Page 74: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Koirat Kennelissä Syöte:12211

void main(){

CDog *kennel[5];CDog *valittuTyyppi;int valinta;

for (int i=0; i<5; i++){ cout<<“(1)Koira (2)Puudeli”; cin >> valinta; switch (valinta) { case 1: valittuTyyppi=new CDog(1,”koira”); break; case 2: valittuTyyppi=new CPoodle(1,”puudeli”); break; }

kennel[i]=valittuTyyppi;}for (i=0; i<5; i++)

kennel[i]->growl();}

Tuloste:GRRRYIPYIPGRRRGRRR

Page 75: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

No mitäs kivaa tuossa oli? Esimerkki esitteli virtuaalifunktoiden

toiminnan puhtaimmillaan Täsmälleen sama koodirivi:

(kennel[i]->growl();) tuotti erilaisia tuloksia Kääntäjä ei kaikissa tapauksissa pysty vielä

käännösaikana päättelemään mitä rajapintafunktion totetusta on tarkoitus kutsua

Päätös tästä siirtyykin ajonaikaiseksi. Tästä käytetään nimitystä dynaaminen sitominen (dynamic binding)

Dynaaminen sitominen mahdollistaa siis sen, että sama jäsenfunktiokutsu käyttäytyy eri tavalla riippuen siitä, minkä tyyppinen olio osoittimen tai viittauksen päässä on

Page 76: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Dynaaminen sitominen Koska growl-funktio on

virtuaalinen, voidaan sen toteutus määritellä uudelleen missä tahansa periytetyssä luokassa.

Niinpä kääntäjä tietää vain, että siinä kutsutaan jotain jäsenfunktion growl toteutusta.

Kääntäjä tuottaa kyseiseen ohjelman kohtaan koodin, joka ensin tarkastaa osoittimen päässä olevan olion todellisen luokan ja vasta sen jälkeen kutsuu sille sopivaa jäsenfunktion toteutusta

void main(){

CDog *kennel[5];CDog *valittuTyyppi;int valinta;

for (int i=0; i<5; i++){ cout<<“(1)Koira (2)Puudeli”; cin >> valinta; switch (valinta) { case 1: valittuTyyppi=new CDog(1,”koira”); break; case 2: valittuTyyppi=new CPoodle(1,”puudeli”); break; }

kennel[i]=valittuTyyppi;}for (i=0; i<5; i++)

kennel[i]->growl();}

Page 77: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Virtuaalifunktoiden hyödyt

Virtuaalifunktiot ja dynaaminen sitominen tekevät mahdollisiksi todella joustavat ohjelmarakenteet jäsenfunktion kutsujan ei tarvitse tietää

yksityiskohtia siitä, mitä jäsenfunktion toteutusta kutsutaan

Ohjelman ylläpidettävyys, laajennettavuus ja luettavuus paranee

Page 78: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Virtuaalifunktioiden hinta Ohjelmakoodin täytyy aina

virtuaalifunktioiden yhteydessä: tarkastaa olion todellinen luokka valita oikea versio jäsenfunktion toteutuksesta

Em. tehtävät jää lähes aina ajonaikaiseksi. valinnan tekeminen hidastaa jäsenfunktion

kutsumista. Käytännön kokemusten mukaan

virtuaalifunktioiden käyttä on n. 4% hitaampaa

Page 79: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Virtuaalifunktioiden hinta Virtuaalifunktiot lisäävät myös muistin

kulutusta: Mikäli luokassa tai sen kantaluokassa on yksikin

virtuaalifunktio, täytyy luokan olioihin tallettaa jonnekkin tieto siitä, minkä luokan olioita ne ovat

Tähän käytetään yleensä virtuaalitaulua (v-taulu) Jokaista luokkaa kohden on yksi v-taulu ja jokaisella

luokan tyyppisellä oliolla on osoitin v-tauluun (virtuaalitaulujen toteutus riippuu kääntäjistä)

osoitin v-tauluun lisää olion muistin kulutusta n. 4 tavun verran. Ylimääräisten virtuaalifunktioiden lisääminen ei kasvata v-taulun osoittimien määrää.

Page 80: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Missä mennään Johdanto

Kertausta Esimerkki Yhteenveto

Luokkien näkyvyysmääreet Perintä Toiminnan korvaaminen Moniperintä Yhteenveto

Page 81: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Moniperintä

Kertausta: Periytymisessä uusi luokka

luodaan periyttämällä siihen jonkin olemassa olevan luokan ominaisuudet.

Miksei sitten periytetä kerralla ominaisuuksia useammasta luokasta?

Page 82: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Moniperintä C++ mahdollistaa moniperinnän. Syntaksi:

class Pegasus : public Bird, public Horse Moniperintä on hyvin kiistelty mekanismi:

Kaikki oliopohjaiset kielet ei tue moniperintää Pyri välttämään moniperinnän käyttöä

moniperinnän käyttö johtaa varsin usein ongelmiin asiat voidaan yleensä ratkaista muillakin tavoilla.

Joskus moniperintä on vain vähemmän työläämpää

Page 83: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Moniperintä -EsimerkkiKirjastonTeos

Hyllyluokka, lainaaika, yms.

KirjastonKirja

(Hyllyluokka, lainaaika, yms.)( Nimi, tekijä, yms.)Mahd. uudet ominaisuudet

Kirja

Nimi, tekijä, yms.

class KirjastonKirja : public KirjastonTeos, public Kirja{

…};

Page 84: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Moniperintä -käyttökohteita Rajapintojen yhdistäminen.

Halutaan oman luokan toteuttavan useiden eri rajapintojen toiminnallisuus

Luokkien yhdistäminen. Halutaan esimerkiksi käyttää hyväksi muutamaa yleiskäyttöistä

luokkaa oman luokan kehitystyössä. Luokkien koostaminen valmiista ominaisuuskokoelmista.

Esimerkki: Kaikki lainaamiseen liittyvät toiminnot on kirjoitettu Lainattava-

luokkaan. Vastaavasti kaikki tuotteen myymiseen liittyvät aisat ovat

luokassa Myytävät. Voimme luoda KirjastonKirja –luokan perimällä sen Kirja-

kantaluokasta ja maustamalla sen Lainattava-luokasta saaduilla ominaisuuksilla

Voimme yhtä lailla luoda KaupallinenCD-ROM-luokan perimällä sen CD-ROM kantaluokasta ja ottaa käyttöön ominaisuudet Myytävä-luokasta

Page 85: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Moniperitytymisen vaaroja Suuri osa moniperiytymisen vaaroista johtuu siitä, että se

on houkuttelevan helpontuntuinen vaihtoehto sellaisissakin tapauksissa, joissa se ei olioajattelun kannalta ole perusteltua.

Moniperityn luokan täytyy olla kaikein aikaa perittyjen kantaluokkiensa ilmentymä

Se ei vaan käy että välillä ollaan yhtä ja välillä toista Esim: Vesitaso ei pysty olemaan yhtäaikaa vene ja lentokone.

Tekee luokkarakenteen vaikeaselkoisiksi Aiheuttaa helposti ongelmia kuten rajapintojen

moniselitteisyyttä ja vaikeuksia elinkaaren hallinnassa. Älä siis käytä moniperintää ellei sille ole painavia

perusteita

Page 86: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Moniperintä ja moniselitteisyys

Kumpaa funktiota kutsutaan kun KirjastonKirjaa pyydetään tulostamaan tiedot?

KirjastonTeos

Hyllyluokka, lainaaika, yms.

KirjastonKirja

(Hyllyluokka, lainaaika, yms.)( Nimi, tekijä, yms.)Mahd. uudet ominaisuudet

Kirja

Nimi, tekijä, yms.

tulostaTiedot()tulostaTiedot()

Page 87: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Moniperintä ja moniselitteisyys Yritys kutsua kahdesta eri kantaluokasta periytynyttä

jäsenfunktiota aiheuttaa C++:ssa käännösaikaisen virheilmoituksen siitä, että jäsenfunktion kutsu on moniselitteinen (ambiguous)

Jos kummankin kantaluokan funktiot tekevät suunnilleen saman asian voidaan ongelma kiertää määrittelemällä samannimiset funktiot virtuaalisiksi (tästä puhutaan myöhemmin). Tällöin kutsutaa aliluokassa toteutettua funktiota

Jos kantaluokan funktiot taas ovat sisällöltään vahvasti erilaisia ajaudumme suurempiin ongelmiin. Tällöin on vaikea saada peritty luokka käyttäytymään siten, että se tyydyttää molemman kantaluokan tarpeet.

Page 88: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Moniperintä ja moniselitteisyys Jos moniselitteisille jäsenfunktioille ei

ole tarkoitus antaa uusia toteutuksia moniperiytetyssä aliluokassa, muodostuu ainoaksi ongelmaksi jäsenfunktion kutsuminen. Tämäkin vain silloin kun kutsutaan suoraan

aliluokan rajapinnan kautta Kantaluokkaosoittimien kautta

moniselitteisyyttä ei ole. Kullakin kantaluokalla on vain yksi

mahdollinen toteutus jäsenfunktiolle

Page 89: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Moniperintä ja moniselitteisyysRatkaisuja Kutsutaan moniselitteistä jäsenfunktiota aina

kantaluokkaosoittimien kautta tarvittaessa vaikkapa väliaikaisia osoitinmuuttujia käyttäen

helpoin, mutta kömpelöin ratkaisu Moniselitteisen jäsenfunktion kutsun yhteydessä on

mahdollista erikseen kertoa, minkä kantaluokan versiota halutaan kutsua. Tämä onnistuu ::-syntaksilla. Esimerkiksi Kirja-luokan tulostaTiedot-jäsenfunktiota voi kutsua syntaksilla:

KirjastonKirja k;k.Kirja::tulostaTiedot()

Tämä syntaksi on kuitenkin myös ehkä hieman oudon näköinen ja vaatii kantaluokan nimen kirjoittamista näkyviin kutsun yhteyteen

Kolmas vaihtoehto on kirjoittaa aliluokkaan uudet keskenään erinimiset jäsenfunktiot, jotka kutsuvat kunkin kantaluokan toteutusta moniselitteiselle jäsenfunktiolle ::-syntaksilla.

Page 90: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Esimerkki (3. vaihtoehto)

classs KirjastonKirja : public KirjastonTeos, public Kirja {public:

.

.

.void tulostaKTeostiedot (std::ostrea& virta) const;void tulostaKKirjatiedot (std::ostrea& virta) const;

}void KirjastonKirja::tulostaKTeostiedot (std::ostrea& virta) const{

KirjastonTeos::tulostaTiedot(virta);}void KirjastonKirja::tulostaKirjatiedot (std::ostrea& virta) const{

Kirja::tulostaTiedot(virta);}

Page 91: Olio-ohjelmoinnin perusteet luento 4: Perinnästä

Mitä tällä luennolla opimme? Puudeli-esimerkin avulla opimme laajentamaan

olemassa olevaa toteutusta perintää käyttämällä Kuinka jäsenmuuttujien ja –funktioiden

näkyvyyttä voidaa hallita luokan sisällä (public, protected, private

2-suuntainen linkitetty lista taas opetti koodin tekemisestä uudelleenkäytettäväksi uudelleenkäytettävän koodin hyväksikäyttöä mitä eroa on public- ja private perinnällä kuinka aggregaatio eroaa private perinnästä

Koirat kennelissä esimerkin avulla opimme dynaamisesta sidonnasta ja virtaalifunktioista

Moniperintä