olio-ohjelmoinnin perusteet luento 7: c++ mallit, standard template library (stl)

66
1 Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL) Jani Rönkkönen [email protected] Luennot muokattu Sami Jantusen ja Kari Smolanderin aikaisempien vuosien luennoista

Upload: kiona-ballard

Post on 03-Jan-2016

60 views

Category:

Documents


2 download

DESCRIPTION

Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL). Jani Rönkkönen [email protected] Luennot muokattu Sami Jantusen ja Kari Smolanderin aikaisempien vuosien luennoista. Sisältö. C++ mallit Yleiskäyttöisyydestä C++ mallit Funktiomallit Luokkamallit - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

1

Olio-ohjelmoinnin perusteetluento 7: C++ mallit, Standard Template Library (STL)

Jani Rönkkö[email protected]

Luennot muokattu Sami Jantusen ja Kari Smolanderin aikaisempien vuosien luennoista

Page 2: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

2

Sisältö C++ mallit

Yleiskäyttöisyydestä C++ mallit Funktiomallit Luokkamallit

Standard Template Library (STL) Taustaa Säiliöt Iteraattorit Algoritmit

Yhteenveto

Page 3: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

3

Yleiskäyttöisestä suunnittelusta Oleellista on pystyä erottamaan pysyvät ja

vaihtelevat asiat toisistaan! Halutaan uudelleenkäyttää pysyvät asiat ja johtaa

uusia luokkia toteuttamalla erikseen vaihtelevat asiat Tavoitteelle löytyy oliopohjaisista kielista erilaisia

mekanismeja Periytyminen on tyypillinen yleiskäyttöisyyteen

liittyvä mekanismi Luokkien yhteiset ominaisuudet voidaan toteuttaa

kertalleen kantaluokassa. Vaihtuvat asiat toteutetaan lapsiluokissa Periytymisen käytöllä on kuitenkin rajansa!

Page 4: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

4

Periytymisen rajoituksia yleiskäytettävyydessä Aliluokat perivät kantaluokkansa

rajapinnan ja toteutuksen kokonaisuudessaan ja täsmälleen samanlaisena! Pysyvään osuuteen kuuluu siis

kokonaisia palveluita (jäsenfunktioita) parametreineen ja paluuvarvoineen.

Todellinen tarve ei kuitenkaan aina ole tällainen!

Page 5: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

5

Muistatko vielä?(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 6: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

6

Esimerkki: 2-suuntainen lista

Pysyvä osa: Listan toteutus (alkioiden

käsittely) Vaihtuva osa:

Alkioiden tyyppi

periytymisen käyttö ei kovin kätevää!

Useat rajapinnoista ovat kuitenkin riippuvaisia alkion tyypistä.

Uudelleenkäytettävyyden taso heikkenee

Deque

IntDeque DoubleDeque

Muistathan vielä 2-suuntaisen listaesimerkkimme?

Page 7: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

7

Entäpä jos?

Mitä jos vaatisimme, että eri tyyppiset alkiot perittäisiin samasta kantaluokasta? Sama 2-suuntainen

listatoteutus toimisi riippumatta alkion todellisesta tyypistä

Alkio

IntAlkio DoubleAlkio

Deque

Page 8: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

8

Ratkaisun huonoja puolia Tyyppitietoisuus häviää

Listaan on mahdollista tallettaa eri tyyppejä sekaisin

Kääntäjä ei pysty tekemään tyyppitarkistuksia

Taulukon käyttäjän tulee itse tietää mitä tyyppiä alkiot oikeasti on (ja tehdä tarvittaessa osoittimen muunnostyöt)

Alkio

IntAlkio DoubleAlkio

Deque

Page 9: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

9

Johtopäätös Erityyppisillä taulukoilla on todella paljon

yhteistä Yhteiset asiat on kuitenkin sen luonteisia, että

niitä on vaikea toteuttaa yleiskäyttöiseksi kantaluokkaan Taulukon alkioiden tyyppi vaikuttaa taulukon

rajapintaan ja toteutukseen kauttaaltaan Alkioiden tyyppien erilaisuuden merkitystä ei voi

eliminoida ilman, että tingitään tyyppiturvallisuudesta ja käyttömukavuudesta

Olisi kiva jos olisi muitakin mekanismeja pysyvyyden ja vaihtelevuuden hallintaan

Page 10: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

10

C++ Mallit (Templates) Käännösaikaisesti tyypitetyissä kielissä (kuten C+

+) täytyy kaikkien parametrien ja muuttujien tyyppien olla selvillä jo ohjelmaa käännettäessä.

Tyyppien geneerisyys ei onnistu ilman lisäkikkoja Mallit (template) ovat C++:n tapa kirjoittaa

yleiskäyttöisiä: funktioita (funktiomallit, function templates) luokkia (Luokkamallit, class templates)

Malleissa tyyppejä ja joitain muitakin asioita voidaan jättää määrämäättä

Auki jätetyt asiat “sidotaan” vasta myöhemmin käytön yhteydessä.

Näin saadaan säilytettyä C++:n vahva käännösaikainen tyypitys, mutta annetaan silti mahdollisuus kirjoittaa geneeristä koodia

Page 11: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

11

Esimerkki C++ mallista Otetaan esimerkiksi meidän 2-suuntainen

lista Muutetaan olemassa olevaa toteutusta ja

käytetään periytymisen sijasta luokkamallia

Toteutetaan Deque-malliin yleinen toiminnallisuus

Jätetään alkioiden tyyppi vielä määrittelemättä

Kun lista otetaan käyttöön, määritellään samalla alkioiden tyyppi tyyliin:

Deque <int> Deque <double>

Auki jätetyt asiat siis selviävät jo käännösaikana, mutta kuitenkin vasta käyttötilanteessa

Deque

Page 12: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

12

C++ mallin syntaksi

template<typename tyyppiparam1, typename tyyppiparam2, ...>//Tähän normaali funktion tai luokan määrittely

TAI VAIHTOEHTOISESTI

template<class tyyppiparam1, class tyyppiparam2, ...>//Tähän normaali funktion tai luokan määrittely

Malli alkaa avainsanalla template Seuraavaksi esitellään kulmasulkeiden sisällä kaikki

auki jätetyt tyyppiparametrit Tämän jälkeen seuraa itse mallin koodi (normaalin

funktion tai luokan tapaan) Tyyppiparametreja voi käyttää koodissa aivan kuin

normaaleja C++ tyyppejä.

Page 13: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

13

Funktiomallit (function template)

Geneerisiä malleja, joista kääntäjä voi generoida eri tyypeillä toimivia funktioita

Kääntäjä osaa automaattisesti päätellä kutsusta tyyppiparametrien arvot

Voidaan kutsua aivan kuin mitä tahansa muuta funktiota: min(1,2); //int min(2.3, 5.7); //double

On myös mahdollista eksplisiittesesti määrätä tyyppiparametreille arvot:float f = min<float>(3.2,6)

Jäsenfunktiomalli on funktiomalli, joka on määritelty luokan jäsenfunktioksi

template <typename T> //Tai template <class T>T min(T p1, T p2){T tulos;if (p1 < p2){

tulos = p1;}else{

tulos = p2;}return tulos

}

Page 14: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

14

Luokkamallit (class template)

Toimii mallina luokille, jotka ovat muuten samanlaisia, mutta joissa jotkin tyypit voivat erota toisistaan

Esimerkkinä vieressä oleva malli Pari:

Ei oma luokkansa vaan malli kokonaiselle “perheelle” luokkia

Kaksi auki jätettyä tyyppiä T1 ja T2 Luokat saadaan luotua määräämällä

tyyppiparametreille arvot. Esimerkki:Pari<int,double> p(1, 3.2);

template <typename T1, typename T2>class Pari{public:Pari(T1 eka, T2 toka);T1 annaEka() const;T2 annaToka() const;...Private: T1 mEka;T2 mToka;

};

template <typename T1, typename T2>Pari<T1, T2>::Pari(T1 eka, T2 toka) :mEka(eka), mToka(toka) //muodostaja{}

template <typename T1, typename T2>T1 Pari<T1, T2>::annaEka() const{return mEka;

}

Page 15: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

15

Ajattelumalli, joka toivottavasti helpottaa luokkamallin käyttöä:

Pari<int,double> p(1, 3.2); Yllä olevassa esimerkissä Pari<int,double> voidaan käsittää

luokan tyyppinä (aivan kuin luokan tyypit myString tai myVeryOwnClass)

void f(Pari<int,int> &i, Pari<float, int*> d); ihan tavallinen funktio jolla on kaksi täysin eri tyyppistä luokkaa

parametrina1. int,int -pari2. float, int* -pari

luokan tyyppi luotava olio

luokan tyyppi luokan tyyppi

Page 16: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

16

Jäsenfunktiomalli malliluokan jäsenenä

template <typename T1, typename T2>class Pari{ . . .template <typename T3, T4>void summaa(const pari<T3, T4>& toinenPari)...

};

template <typename T1, typename T2>template <typename T3, typename T4>Void Pari<T1, T2>::summaa(const Pari<T3,T2>& toinenPari){ mEka += toinenPari.annaEka(); mToka += toinenPari.annaToka();}

Luokkamallissa ja sen sisällä jäsenfunktiomallissa on nyt molemmissa aukijätettyjä parametreja:

Malli mallin sisällä Mihin tahansa pariin voi

nyt summata minkä tahansa tyyppisen toisen parin, kunhan parien alkiot voi laskea yhteen keskenään

Page 17: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

17

Tyyppiparametreille asetetut vaatimukset C++ mallin koodin on käännyttävä, kun

tyyppiparametrien tilalle sijoitetaan todelliset tyypit Esimerkiksi aiemmin määrittelemämme min –

funktiomallin parametrien pitää olla sellaisia, että niiden arvoja voi vertailla < operaattorilla

Jos mallin koodi kutsuu auki jätetyn tyypin kautta esim. olion jäsenfunktiota, voi mallia käyttää vain sellaisten tyyppien kanssa, mistä kyseinen jäsenfunktio löytyy

Page 18: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

18

Tyyppiparametreille asetetut vaatimukset

Koska tyyppiparametrien vaatimukset on siroteltu mallin koodin sekaan, tyyppiparametreihin kohdistuvat vaatimukset olisi hyvä dokumentoida ja kerätä yhteen paikkaan, josta mallin käyttäjä voi helposti ne tarkistaa

Jotta malli pysyisi mahdollisimman yleiskäyttöisenä, tulisi se suunnitella vaatimaan tyyppiparametreiltaan mahdollisimman vähän

Page 19: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

19

Esimerkki Aiemmin esitelty min-

malli vaatii kopiomuodostimen ja sijoitusoperaattorin olemassaolon tyyppiparametreiltaan Rajoittaa mallin käyttöä,

koska kaikilla luokilla ei näitä ole

Alla on parempi versio min-mallista Turhat sijoitukset on

jätetty pois ja parametrit välitetään viitteinä, joten vaaditaan vain vertailuoperaattori

template <typename T> T min(T p1, T p2){T tulos;if (p1 < p2){

tulos = p1;} else {

tulos = p2;}return tulos

}

template <typename T> const T& min(const T& p1, const T& p2){if (p1 < p2){

return p1;} else {

return p2;}

}

Page 20: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

20

Mallien oletusparametrit Malleilla voi olla oletustyyppiparametreja, samaan

tapaan kuin normaaleilla funktiolla oletusarvoja, esimerkiksi:template<typename T1=int, typename T2=T1>class Pari2{…};

Liukuluku-merkkijono yhdistelmä:Pari2<double,string>

Molemmat arvot liukulukuja:Pari2<double>

Molemmat arvot kokonaislukuja:Pari2<>

Page 21: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

21

Mallien vakioparametrit Malleissa voi jättää auki tiettyjä

käännösaikaisia vakioita, jotka jäävät mallin parametreiksi, kelvollisia ovat:

Kokonaislukuvakiot ja enum tyyppiset arvot

Osoitin tai viite globaaliin olioon tai funktioon

Osoitin jäsenfunktioon tai muuttujaan

Jokainen eri vakioparametrin arvolla luotu luokka on oma erillinen luokkansa

Esimerkin eripituiset merkkijono-oliot (s1 ja s2) eivät siis kuulu samaan luokkaan, eikä niitä voi esimerkiksi sijoittaa toisiinsa

template<unsigned long SIZE>class Mjono{public: Mjono(const char *arvo); const char* annaArvo() const;private: char taulukko[SIZE+1];};

Mjono<12> s1(“Tuli täyteen”);Mjono<7> s2(”Lyhempi”);

Page 22: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

22

Malliparametrit Mallissa voi jättää auki myös

toisen mallin Esimerkissä on funktiomalli,

jossa on jätetty auki yksi kaksi tyyppiparametria saava malli ja yksi tavallinen tyyppiparametri

Mallin summaa avulla voi nyt summata mistä tahansa kaksiparametrisesta luokkamallista instantuoituja olioita, kunhan tyyppiparametrit ovat samat ja se tarjoaa metodit annaEka ja annaToka

template<template<typename T1, typename T2> class X, typename S>S summaa(const X<S, S>& x){ return x.annaEka() + x.annaToka();}

void kayta(){ Pari<int, int> p(1,2); int tulos=summaa(p);}

Page 23: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

23

Mallien erikoistus

On mahdollista määrittää erityinen versio (juuri tietyille tyypeille) mallista

Käyttäjän kannalta kaikki säilyy ennallaan, kääntäjä valitsee erikoistus koodin tarvittaessa

Esimerkissä on ideana vähentää muistinkulutusta tallentamalla bool tyyliset arvot yhteen char muuttujaan ekaJaToka, jota sitten käsitellään bittioperaattoreilla

Luokkamalleille on mahdollista tehdä myös osittaiserikoistus, jolloin vain osa tyypeistä sidotaan

template <>class Pari<bool, bool>{public:Pari(bool eka, bool toka);bool annaEka() const;bool annaToka() const;...private: unsigned char ekaJaToka;

};

bool Pari<bool, bool>::annaEka() const{return (ekaJaToka & 1) != 0;

}

Tyhjät kulmasulkeet merkiksi siitä, että erikoistumisessa ei ole enää auki jääneitä tyyppejä

Kirjoitettu auki ne tyypit mitä erikoistumiinen koskee

Page 24: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

24

Mallin koodin sijoittelu Koska mallin koodin on oltava kääntäjän

tiedossa mallia käytettäessä, on mallin koodi kirjoitettava kokonaisuudessaan otsikkotiedostoon

Lisäämällä avainsana export ennen template sanaa sekä esittelyn että toteutuksen yhteyteen on periaatteessa mahdollista siirtää mallin koodi erilliseen .cpp tiedostoon Valitettavasti suurin osa kääntäjistä ei tue

tätä ominaisuutta ja siksi sitä ei tulisi käyttää

Page 25: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

25

Missä mennään? C++ mallit

Yleiskäyttöisyydestä C++ mallit Funktiomallit Luokkamallit

Standard Template Library (STL) Taustaa Säiliöt Iteraattorit Algoritmit

Yhteenveto

Page 26: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

26

STL (Standard Template Library)

Hyvä esimerkki C++ mallien käytöstä ja geneerisestä ohjelmoinnista

Määrittelee joukon tavallisimpia tietorakenteita ja niiden käyttöön tarkoitettuja algoritmeja Tarkoituksena on uudelleenkäyttää STL:n

optimoituja tietorakenteita sen sijaan, että “keksittäisiin pyörä uudelleen”

Page 27: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

27

STL:n rakenne Säiliöt (container) ovat STL:n tarjoamia tietorakenteita Iteraattorit (iterator) ovat “kirjanmerkkejä” säiliöiden

läpikäymiseen Geneeriset algoritmit (generic algorithm) käsittelevät säiliöitä

iteraattoreiden avulla Säiliösovittimet (container adaptor) ovat säiliömalleja, jotka

toteutetaan halutun toisen säilion avulla. Niillä voi muuntaa säiliön rajapinnan toisenlaiseksi

Funktio-oliot (function object) ovat olioita, jotka käyttäytyvät kuten funktiot ja joita voi käyttää muun muassa algoritmien toiminnan säätämiseen

Varaimet (allocator) ovat olioita säiliöiden muistinhallinnan räätälöintiin. Lyhyesti selitettynä varaimet ovat olioita, jotka osaavat varata ja vapauttaa muistia. Normaalisti STL:n säiliöt varaavat muistinsa new’llä ja vapauttavat deletellä. Jos niille annetaan ylimääräisenä tyyppiparametrina varainluokka, ne käyttävät ko.luokan palveluita tarvitsemansa muistin varaamiseen ja vapauttamiseen

Page 28: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

28

STLAlgoritmien geneerisyys Olemme olio-ohjelmoijina tottuneet siihen, että halutut tiedot

ja toiminnallisuudet kirjoitetaan luokan jäsenfunktioihin. Tuntuisi siis loogiselta, että STL:n tarjoamat algoritmit olisi

myös toteutettu säiliöiden jäsenfunktiona STL:ssä lähes kaikki algoritmit on kuitenkin kirjoitettu

irrallisina funktioina. Miksi? Algoritmien geneerisyys on laajempaa kuin pelkkä tyypin auki

jättäminen Tyypin lisäksi STL:n algoritmit jättävät mm. auki sen minkä

tietorakenteen kanssa ne toimivat. Näin samaa geneeristä find-algoritmia voidaan käyttää niin listan,

vektorin kuin joukonkin kanssa. Algoritmiä ei myöskään tarvitse kirjoittaa erikseen jokaiselle

säiliölle vaan yksi geneerinen funktiomalli toimii kaikkien säiliöiden kanssa

Geneerisyydestä seuraa myös se, että STL:n algoritmeja voi käyttää myös omien tietorakenteiden kanssa (kunhan ne vain toteuttavat STL:n algoritmien asettamat vaatimukset)

Page 29: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

29

Iteraattorit On hyvin tavallista, että tietorakenteen alkioita

käydään läpi järjestyksessä yksi kerrallaan Tyypillinen ratkaisuyritys ongelmaan on lisätä

läpikäymiseen tarvittavat operaatiot itse tietorakenteeseen

annaEnsimmainen annaSeuraava onkoLoppu …

Tällöin säiliö muistaisi itse, missä alkiossa läpikäyminen on sillä hetkellä menossa

Page 30: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

30

Iteraattorit

säiliön tila koostuisi sekä listan alkioista että läpikäyntipaikasta. operaatiot kuten annaEnsimmainen() ja

annaSeuraavat() muuttaisivat väistämättä säiliön tilaa

kyseiset operaatiot eivät voisi olla vakiojäsenfunktioita vakioviitteen päässä olevaa listaa ei voisi täten selata

läpi Usein on tarve lukea listaa kahdesta

kohtaa yhtä aikaa.

Edellä ehdotetun ratkaisumallin ongelmat ovat:

Page 31: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

31

Iterator Luodaan kaksi eri luokkaa

Varsinainen säiliö, joka ei sisällä mitään paikkatietoa

“kirjanmerkki”, joka vaan muistaa, missä kohtaa säiliötä ollaan läpikäymässä

Kyseessä on tunnettu Design Pattern (Iterator)

Page 32: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

32

Iteraattorit Erittäin tärkeä käsite STL:ssä Jokaista säiliötyyppiä kohden on olemassa

myös iteraattorityyppi, jonka avulla säiliön alkiot voi käydä läpi

Iteraatorin voi käsittää kirjainmerkkinä, joka muistaa tietyn paikan tietyssä säiliössä. Iteraattoria voi siirrellä säiliön sisällä Iteraattorin läpi voi lukea ja muuttaa säiliön

alkioita Syntaksi vastaa osoitinaritmetiikkaa

Page 33: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

33

Iteraattorikategoriat Syöttöiteraattori (input iterator) voi vain

lukea alkioita Tulostusiteraattori (output iterator) voi vain

muuttaa alkioita (ei lukea) Eteenpäin-iteraattori (forward iterator) voi

lukea ja muuttaa alkioita ja lisäksi iteraattoria voi siirtää yhdellä eteenpäin.

Kaksisuuntainen iteraattori (bidirectional iterator) voi lukea, muuttaa ja liikkua kumpaakin suuntaan

Hajasaanti-iteraattori (random access iterator) on kuin kaksisuuntainen iteraattori, mutta se voi harpata mielivaltaisia askelia.

Page 34: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

34

Iteraattorit ja säiliöt Iteraattorit liittyvät kiinteästi säiliöihin,

joten STL:ssä iteraattoriluokat ja iteraattoreiden luominen on siirretty säiliöluokkien sisälle

Jokainen STL säiliöluokka määrittelee kaksi luokan sisäistä tyyppiä: iterator ja const_iterator deque ja vector käyttävät hajasaanti iteraattoria list, set, multiset, map ja multimap käyttävät

kaksisuuntaista iteraattoria

Page 35: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

35

Esimerkki, säiliön läpikäynti iteraattoreilla

void nollaaAlkiot(vector<int>& vektori){

vector<int>::iterator i=vektori.begin(); //alkuunwhile (i!=vektori.end()) //toistetaan, kunnes ollaan lopussa{

*i=0; //alkion arvoksi nolla++i; //siirrytään seuraavaan alkioon

}}void tulostaAlkiot(const vector<int>& vektori){

for(vector<int>::const_iterator i=vektori.begin(); i!=vektori.end(); ++i) //const_iterator, koska ei muuteta arvoa{

cout << *i << ” ”;}cout << endl;

}

Page 36: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

36

Iteraattoreiden kelvollisuus

Jos säiliöön lisätään tai siitä poistetaan alkioita, osa olemassa olevista iteraattoreista (tai osoittimista/viitteistä) voi muuttua kelvottomiksi

Kelvotomalle iteraattorille ainoat sallitut operaatiot ovat tuhoaminen ja uuden arvon sijoitus

Vector: Lisäys voi mitätöidä kaikki iteraattorit, osoittimet ja viitteet, jos joudutaan

tekemään uusi muistinvaraus, muuten lisäyspaikan jälkeiset (jos vektorille on varattu riittävästi tilaa ennakkoon reserve jäsenfunktiolla)

Poisto mitätöi kaikki poistopaikan jälkeiset Deque:

Lisäys alkuun tai loppuun mitätöi kaikki iteraattorit. Osoittimet ja viittaukset säilyvät. Lisäys keskelle mitätöi kaikki.

Poisto alusta tai lopusta mitätöi alkioon itseensä osoittavat osoittimet ja viitteet ja poistetun vasemmalla puolella olevan iteraattorin. Poisto keskeltä mitätöi kaikki.

List ja assosiatiiviset säiliöt: Lisääminen ei mitätöi mitään Poisto mitätöi alkioon itseensä osoittavat osoittimet ja viitteet ja

poistetun vasemmalla puolella olevan iteraattorin.

Page 37: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

37

Iteraattorisovittimet Iteraattorisovittimet ovat erikoisiteraattoreita,

jotka toimivat hieman eri tavalla kuin perusiteraattorit:

Käänteisiteraattorit (reverse iterator) Tavallisen iteraattorin peilikuva, kaikki operaatiot

toimivat nurinkurisesti, esim. ++ menee taaksepäin ja -- eteenpäin.

Jäsenfunktio rbegin palauttaa käänteisiteraattorin säiliön loppuun ja rend säiliön alkuun

Lisäysiteraattorit (insert iterator/inserter) Voidaan käyttää lisäämään alkioita säiliöön

Virtaiteraattorit (stream iterator) Lukevat ja kirjoittavat tiedostovirtoihin säiliöiden sijasta

Page 38: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

38

STL:n säiliöt STL:n säiliöt muistuttavat suuresti toisiaan

Rajapinnat ovat yhtenäisiä, eli suurin osa yhden säiliön jäsenfunktioista löytyy myös muista STL säiliöistä

Kun oppii käyttämään yhtä muiden käytön oppiminen on helppoa

Säiliöt jakautuvat pääpiirteittään kahteen kategoriaan:

Sarjat (sequence) ovat säiliöitä, joiden alkioita pystyy hakemaan niiden järjestysnumeron perusteella. Samoin alkioita voi lisätä haluttuun paikkaan ja poistaa siitä. Esimerkki: vector

Assosiatiiviset säiliöt (associative container) puolestaan perustuvat siihen, että alkioita haetaan säiliöstä avaimen (key) perusteella. Esimerkiksi puhelinluettelo muistuttaa assosiatiivista säiliötä– siinä numeron pystyy etsimään nopeasti nimen perusteella

Page 39: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

39

Säiliöiden käyttöohjeetEsim. Visual C++ online help

Page 40: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

40

STL:n alkioista Alkioiden tyypeillä tulee olla:

Kopiomuodostin, joka luo alkuperäisen olion kanssa samanlaisen olion

sijoitusoperaattori, jonka tuloksena sijoituksen kohteena olevasta oliosta tulee samanlainen sijoitetun olion kanssa

STL:n assosiatiiviset säiliöt vaativat, että alkioiden avaimia voi myös kopioida ja sijoitaa ja lisäksi kahta avainta täytyy pystyä vertailemaan

Viitteet eivät kelpaa alkioiksi

Page 41: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

41

Sarjat-”peräkkäissäiliöt” Sarjat ovat säiliöitä, joissa alkiot

sijaitsevat “peräkkäin” ja joissa jokaisella alkiolla on järjestysnumero Alkioita voi selata järjestyksessä Halutun alkion voi hakea järjestysnumeron

perusteella Uusia alkioita voi lisätä säiliössä haluttuun

paikkaan ja vanhoja voi poistaa Kaikissa sarjoissa annetaan sarjan

alkioiden tyyppi mallin tyyppiparametrina. Esim: vector<float>, deque<int> ja list<string>

Page 42: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

42

Sarjat-”peräkkäissäiliöt” STL:n sarjasäiliöt: vector

STL vastine taulukoille. Tehokas jos tietorakennetta indeksoidaan paljon ja alkioita poistetaan tai lisätään vain loppuun.

deque Kaksipäinen taulukko (pakka). Tehokkaampi kuin

vektori, jos alkioiden lisäyksiä tai poistoja halutaan tehdä myös alkuun. Indeksointi hieman vektoria hitaampaa.

list Kaksisuuntainen linkitetty lista. Nopea poistaa tai

lisätä alkioita minne tahansa, indeksointi hidasta.

Page 43: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

43

Sample Code:

////////////////////////////////////////////////////////////////////////// front.cpp: Illustrates the vector::front and vector::back methods.//// Functions://// front - Returns reference to first element.//// back - Returns reference to last element.//// push_back - Appends (inserts) an element to the end of a// container, allocating memory for it if necessary.// pop_back - Deletes the last element.//// size - Returns number of elements.//// begin - Returns an iterator to start traversal.//// end - Returns an iterator for the last element.//// erase - Deletes elements (single & range).////////////////////////////////////////////////////////////////////////

Vector esimerkki (1/3)

Page 44: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

44

cout << "First element: " << theVector.front() << endl;

cout << "Last element: " << theVector.back() << endl;

cout << "Elements in vector: " << theVector.size() << endl;

// Delete the last element of the vector. Remember that the vector is

// 0-based, so theVector.end() actually points 1 element beyond the end.

theVector.erase(theVector.end() - 1);

//Erase the last element with pop_back()

theVector.pop_back();

cout << endl << "After erasing 2 last elements, new last element is: "

<< theVector.back() << endl;

// Delete the first element of the vector.

theVector.erase(theVector.begin());

cout << "After erasing first element, new first element is: "

<< theVector.front() << endl;

cout << "Elements in vector: " << theVector.size() << endl;

}

#include <iostream>#include <vector>

using namespace std ;

typedef vector<int> INTVECTOR;

const ARRAY_SIZE = 4;

void main(){ // Dynamically allocated vector begins with 0 elements. INTVECTOR theVector;

// Intialize the array to contain the members [100, 200, 300, 400] for (int cEachItem = 0; cEachItem < ARRAY_SIZE; cEachItem++) theVector.push_back((cEachItem + 1) * 100);

Vector esimerkki (2/3)

Page 45: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

45

Program Output is:=====================

First element: 100Last element: 400Elements in vector: 4

After erasing 2 last element, new last element is: 200After erasing first element, new first element is: 200Elements in vector: 1

Vector esimerkki (3/3)

Page 46: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

46

Deque esimerkki (1/3)

Sample Code:

////////////////////////////////////////////////////////////////////// // deque.cpp ://// Functions://// operator[] - Indexing// at – Same as above.// empty – Is the container empty// push_front – Inserts a new element at the beginning// pop_front – Removes the first element//////////////////////////////////////////////////////////////////////

Page 47: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

47

//print out the contents

print_contents (a,"a");

cout <<"The first element of a is " <<a[0] <<endl;

cout <<"The first element of a is " <<a.at(0) <<endl;

cout <<"The last element of a is " <<a[a.size()-1] <<endl;

cout <<"The last element of a is " <<a.at(a.size()-1) <<endl;

//remove the first element

a.pop_front();//print out the contents print_contents (a,"a");

}

//function to print the contents of deque

void print_contents (CHARDEQUE deque, char *name)

{

CHARDEQUE::iterator pdeque;

cout <<"The contents of "<< name <<" : ";

for(pdeque = deque.begin(); pdeque != deque.end(); pdeque++)

{

cout << *pdeque <<" " ;

}

cout<<endl;

}

#include <iostream>#include <deque>

using namespace std;

typedef deque<char > CHARDEQUE;void print_contents (CHARDEQUE deque, char*);

void main(){ //create an empty deque a CHARDEQUE a;

//check whether it is empty if(a.empty()) cout<<"a is empty"<<endl; else cout<<"a is not empty"<<endl;

//inset A, B, C and D to a a.push_back(‘C’); a.push_front(‘B’); a.push_front(‘A’); a.push_back(‘D’);

//check again whether a is empty if(a.empty()) cout<<"a is empty"<<endl; else cout<<"a is not empty"<<endl;

Deque esimerkki (2/3)

Page 48: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

48

Deque esimerkki (3/3)Program Output is:===============a is emptya is not emptyThe contents of a : A B C DThe first element of a is AThe first element of a is AThe last element of a is DThe last element of a is DThe contents of a : B C D

Page 49: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

49

list esimerkki (1/3)

Sample Code:

////////////////////////////////////////////////////////////////////////// assign.cpp : Shows the various ways to assign and erase elements// from a list<T>.//// Functions://// assign(p1,p2) – Replaces the contents of container with // p1 elements of p2 / or from p1 to p2 // clear – Empties the container// insert - Inserts an element ////////////////////////////////////////////////////////////////////////

Page 50: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

50

list esimerkki (2/3)#include <list>#include <iostream>

using namespace std ;

typedef list<int> LISTINT;

void main(){ LISTINT listOne; LISTINT listAnother; LISTINT::iterator i;

// Add some data listOne.push_front (2); listOne.push_front (1); listOne.push_back (3);

listAnother.push_front(4); //replace contents of listAnother with contents of

listOne listAnother.assign(listOne.begin(), listOne.end());

// 1 2 3 for (i = listAnother.begin(); i != listAnother.end(); ++i) cout << *i << " "; cout << endl;

listAnother.assign(4, 1);

// 1 1 1 1 for (i = listAnother.begin(); i != listAnother.end(); ++i) cout << *i << " "; cout << endl; //1 2 1 1 1 i=listAnother.begin(); i++; listAnother.insert(i,2);

for (i = listAnother.begin(); i != listAnother.end(); ++i) cout << *i << " "; cout << endl; //empty the whole list listAnother.clear(); if (listAnother.empty()) cout << "All gone” << endl;}

Page 51: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

51

list esimerkki (3/3)

Program Output is:

1 2 31 1 1 11 2 1 1 1All gone

Page 52: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

52

Assosiatiiviset säiliöt (associative container)

Eroavat sarjoista siinä, että alkioita ei lueta, lisätä tai poisteta niiden “sijainnin” tai järjestysnumeron perusteella

Jokaiseen alkioon liittyy avain (key), jonka perusteella alkion voi myöhemmin hakea

WorkDesk ReturnedItemJobID : IntegerJobID : Integer

Page 53: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

53

Assosiatiiviset säiliötyypit set (Joukko)

Yksinkertaisin säiliötyyppi, missä avaimena on alkio itsessään

Vain yksi samanarvoinen alkio Käytännöllinen, jos ohjelmassa pidetään yllä rekisteriä,

johon lisätään alkioita sekalaisessa järjestyksessä ja pitää nopeasti tietää, löytyykö tietty alkio rekisteristä.

multiset (monijoukko) Eroaa setistä siten, että samanarvoisia alkioita voi olla

useita ja joukolta voi kysyä montako annetun arvoista alkiota joukossa on

map (assosiaatiotaulu) Tietorakenne, missä avain ja alkio ovat erillisiä ja jossa

avaimen perusteella voidaan hakea haluttu alkio. Voidaan myös ajatella eräänlaisena taulukkona

Kutakin avainta vastaa yksi alkio multimap (assosiaatiomonitaulu)

Eroaa mapista siten, että yhtä avainta kohden voi olla useita alkioita

Page 54: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

54

set esimerkki (1/3)//////////////////////////////////////////////////////////////////////////// SetFind.cpp:// Illustrates how to use the find function to get an iterator// that points to the first element in the controlled sequence// that has a particular sort key.//// Functions://// find Returns an iterator that points to the first element// in the controlled sequence that has the same sort key// as the value passed to the find function. If no such// element exists, the iterator equals end().// insert Inserts only, if the element was not in the set before//////////////////////////////////////////////////////////////////////

Page 55: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

55

set esimerkki (2/3)

// The debugger can't handle symbols more// than 255 characters long. STL often//creates symbols longer than that. When//symbols are longer than 255 characters, //the warning is issued.#pragma warning(disable:4786)#include <set>#include <iostream>

using namespace std ;

typedef set<int> SET_INT;

void truefalse(int x){ cout << (x?"True":"False") << endl;}

void main() { SET_INT s1; //insert some data cout << "s1.insert(5)" << endl; s1.insert(5); cout << "s1.insert(8)" << endl; s1.insert(8); cout << "s1.insert(12)" << endl; s1.insert(12);

SET_INT::iterator it; cout << "it=find(8)" << endl; it=s1.find(8); cout << "it!=s1.end() returned "; truefalse(it!=s1.end()); // True

cout << "it=find(6)" << endl; it=s1.find(6); cout << "it!=s1.end() returned "; truefalse(it!=s1.end()); // False}

Page 56: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

56

set esimerkki (3/3)

Program Output is:=============s1.insert(5)s1.insert(8)s1.insert(12)it=find(8)it!=s1.end() returned Trueit=find(6)it!=s1.end() returned False

Page 57: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

57

map esimerkki (1/3)

//////////////////////////////////////////////////////////////////////// Asks numbers from the user and prints them as words// // value_type is a definition of Pair type for map, with // elements first and second, defined as follows:// typedef pair<const Key, T> value_type;//// template<class T, class U> // struct pair { // typedef T first_type;// typedef U second_type // T first; // U second; // pair(); // pair(const T& x, const U& y); // template<class V, class W> // pair(const pair<V, W>& pr); // }; //////////////////////////////////////////////////////////////////////

Page 58: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

58

map esimerkki (2/3)#pragma warning(disable:4786)

#include <iostream>#include <string>#include <map>

using namespace std;//the key is type int, value is type stringtypedef map<int, string > INT2STRING;

void main(){// 1. Create a map of ints to strings INT2STRING theMap; INT2STRING::iterator theIterator; string theString = ""; int index;

// Fill it with the digits 0 - 9, each mapped to its string counterpart// value_type descripes an element of the controlled sequence... theMap.insert(INT2STRING::value_type(0,"Zero")); theMap.insert(INT2STRING::value_type(1,"One")); theMap.insert(INT2STRING::value_type(2,"Two")); theMap.insert(INT2STRING::value_type(3,"Three")); theMap.insert(INT2STRING::value_type(4,"Four")); theMap.insert(INT2STRING::value_type(5,"Five")); theMap.insert(INT2STRING::value_type(6,"Six")); theMap.insert(INT2STRING::value_type(7,"Seven")); theMap.insert(INT2STRING::value_type(8,"Eight")); theMap.insert(INT2STRING::value_type(9,"Nine"));

// Read a Number from the user and print it back as words for( ; ; ) { cout << "Enter \"q\" to quit, or enter a Number: "; cin >> theString; if(theString == "q") break; // extract each digit from the string, find its corresponding // entry in the map (the word equivalent) and print it for(index = 0; index < theString.length(); index++){ theIterator = theMap.find(theString[index] - '0'); if(theIterator != theMap.end() ) // is 0 - 9 cout << (*theIterator).second << " "; else // some character other than 0 - 9 cout << "[err] "; } cout << endl; }}

Page 59: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

59

map esimerkki (3/3)

Program Output is:==============Enter "q" to quit, or enter a Number: 22Two TwoEnter "q" to quit, or enter a Number: 33Three ThreeEnter "q" to quit, or enter a Number: 456Four Five SixEnter "q" to quit, or enter a Number: q

Page 60: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

60

Muita säiliöitä vector<bool> (totuusarvovektori)

Vektorin erikoistapaus boolean arvoille, säästää muistia

bitset (bittivektori) Tarkoitettu binääristen bittisarjojen

käsittelyyn Säiliösovittimet

stack (Pino) queue (Jono) priority_queue (prioriteettijono)

jono, jossa alkiot suuruusjärjestyksessä

Page 61: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

61

STL algoritmit STL tarjoaa monenlaisia algoritmeja säiliöiden

käsittelyyn Näille annetaan parametreina iteraattoreita:

Iteraattoreiden avulla algoritmi voi toimia vain osalle säiliötä. Väli määrätään antamalla parametrina kaksi iteraattoria (jos halutaan tehdä toiminto koko säiliölle voi välinä antaa begin ja end kutsujen palauttamat iteraattorit)

Sama algoritmi toimii erilaisten säiliöiden kesken, koska itse säiliöiden tyyppiä ei tarvitse kertoa algoritmille

Iteraatorisovittimien avulla voi vaikuttaa algoritmin toimintaan. Esimerkiksi find-algoritmi etsii normaalisti ensimmäisen halutunarvoisen alkion, mutta käänteisiteraattorilla viimeisen

Ohjelmoija voi kirjoittaa omia iteraattorityyppejä, joilla STL algoritmit voi sovittaa lähes mihin tahansa tietorakenteeseen

Page 62: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

62

STL algoritmit Algoritmit saa käyttöön: #include <algorithm> copy (alku, loppu, kohde)

Kopioi välillä alku-loppu olevat alkiot iteraattorin kohde päähän. Normaaleilla iteraattoreilla korvaa vanhat arvot, lisäysiteraattorilla lisää uusia.

find (alku, loppu, arvo) Etsii väliltä alku-loppu ensimmäisen alkion, jonka

arvo on arvo, jos sopivaa ei löydy, palautetaan loppu sort (alku, loppu)

Järjestää välillä alku-loppu olevat alkiot suuruusjärjestykseen. Parametrien tulee olla hajasaanti iteraattoreita (list säiliöllä on sort jäsenfunktiona)

Page 63: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

63

STL algoritmit merge (alku1, loppu1, alku2, loppu2, kohde)

Yhdistää välien alku1-loppu1 ja alku2-loppu2 alkiot ja kopioi ne suuruusjärjestyksessä iteraattorin kohde päähän, kuten copy. lähtötietojen pitää olla valmiiksi suuruusjärjestyksessä.

for_each (alku, loppu, funktio) Antaa jokaisen väliltä alku-loppu olevan alkion vuorollaan

funktion parametriksi ja kutsuu funktiota partition (alku, loppu, ehtofunktio)

järjestää välillä alku-loppu olevat alkiot niin, että ensin tulevat ne alkiot, joille ehtofunkio palauttaa true ja sitten ne joilla se palautta false

random_shuffle (alku, loppu) Sekoittaa välillä alku-loppu olevat alkiot satunnaiseen

järjestykseen. Algoritmille voi ylimääräisenä parametrina antaa satunnaislukugeneraattorin.

Page 64: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

64

Esimerkki#include <iostream>#include <algorithm>#include <vector>using namespace std;

//aliohjelma vektorien tulostukseenvoid printVector( vector<int> vektori){ vector<int>::iterator iter; for(iter = vektori.begin(); iter != vektori.end(); iter++) { cout << *iter <<" " ; } cout<<endl;}

void main (){ vector<int> vektori; for (int i=0;i<12;i++) //lisätään alkioita vektoriin vektori.push_back(i);

printVector(vektori); //tulostetaan vektori //satunnainen järjestys random_shuffle(vektori.begin(), vektori.end()); printVector(vektori);

int eka = vektori.front(); //ensimmäinen alkio talteen int vika = vektori.back(); //viimeinen alkio talteen sort(vektori.begin(), vektori.end()); //järjestetään vektori printVector(vektori); vector<int>::iterator ekanpaikka = find(vektori.begin(), vektori.end(),eka); //etsitään eka vector<int>::iterator vikanpaikka = find(vektori.begin(), vektori.end(),vika); //etsitään toka //poistetaan alkiot eka <= alkio < vika vektori.erase(ekanpaikka,vikanpaikka); printVector(vektori); }

Tulostus esimerkiksi:0 1 2 3 4 5 6 7 8 9 10 114 3 0 2 10 7 8 11 5 1 6 90 1 2 3 4 5 6 7 8 9 10 110 1 2 3 9 10 11

Page 65: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

65

Mitä tänään opimme? Haluamme tehdä geneeristä koodia.

Valitettavasti periytyminen ei aina ratkaise pulmiamme.

Joissain tilanteissa C++ mallien käyttö on hyvä työkalu geneerisyyteen

C++ malleja on sekä luokka- että funktiomallit

Standard Template Library (STL) Hyvä esimerkki siitä miten C++ mallien avulla

saadaan geneeristä koodia STL:n tarjoamista palveluista opeteltiin käyttämään

Säiliötä Iteraattoreita Algoritmeja

Page 66: Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL)

66

Tentti + palaute

Tentti keskiviikkona 5.3.2008

Kurssista tulee palautekysely, josta ilmoitan sähköpostilla.