1 / 66

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

Olio-ohjelmoinnin perusteet luento 7: C++ mallit, Standard Template Library (STL). Jani Rönkkönen jani.ronkkonen@lut.fi Luennot muokattu Sami Jantusen ja Kari Smolanderin aikaisempien vuosien luennoista. Sisältö. C++ mallit Yleiskäyttöisyydestä C++ mallit Funktiomallit Luokkamallit

Download Presentation

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

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Olio-ohjelmoinnin perusteetluento 7: C++ mallit, Standard Template Library (STL) Jani Rönkkönen jani.ronkkonen@lut.fi Luennot muokattu Sami Jantusen ja Kari Smolanderin aikaisempien vuosien luennoista

  2. Sisältö • C++ mallit • Yleiskäyttöisyydestä • C++ mallit • Funktiomallit • Luokkamallit • Standard Template Library (STL) • Taustaa • Säiliöt • Iteraattorit • Algoritmit • Yhteenveto

  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!

  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!

  5. Muistatko vielä?(IntDeque) Otetaanpa Deque luokka hyötykäyttöön! • Luodaan Int-pohjainen lista • 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(); class IntDeque: public Deque { public: IntDeque(); void insert (int); void remove(); int current () const; ~IntDeque(); }; Deque-luokasta peritty Uusi toiminnallisuus next prev data next prev data next prev data

  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?

  7. Entäpä jos? • Mitä jos vaatisimme, että eri tyyppiset alkiot perittäisiin samasta kantaluokasta? • Sama 2-suuntainen listatoteutus toimisi riippumatta alkion todellisesta tyypistä Deque Alkio IntAlkio DoubleAlkio

  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) Deque Alkio IntAlkio DoubleAlkio

  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

  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

  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

  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ä.

  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 • }

  14. Luokkamallit (class template) • template <typename T1, typename T2> • class Pari • { • public: • Pari(T1 eka, T2 toka); • T1 annaEka() const; • T2 annaToka() const; • . • . • . • Private: • T1 mEka; • T2 mToka; • }; • 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> • 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; • }

  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 parametrina • int,int -pari • float, int* -pari luotava olio luokan tyyppi luokan tyyppi luokan tyyppi

  16. Jäsenfunktiomalli malliluokan jäsenenä • 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 • 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(); }

  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

  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

  19. Esimerkki • template <typename T> • T min(T p1, T p2) • { • T tulos; • if (p1 < p2) • { • tulos = p1; • } else { • tulos = p2; • } • return tulos • } • 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> • const T& min(const T& p1, const T& p2) • { • if (p1 < p2) • { • return p1; • } else { • return p2; • } • }

  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<>

  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”);

  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); • }

  23. Mallien erikoistus Tyhjät kulmasulkeet merkiksi siitä, että erikoistumisessa ei ole enää auki jääneitä tyyppejä • 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; • } Kirjoitettu auki ne tyypit mitä erikoistumiinen koskee

  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ää

  25. Missä mennään? • C++ mallit • Yleiskäyttöisyydestä • C++ mallit • Funktiomallit • Luokkamallit • Standard Template Library (STL) • Taustaa • Säiliöt • Iteraattorit • Algoritmit • Yhteenveto

  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”

  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

  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)

  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

  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:

  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)

  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

  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.

  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

  35. Esimerkki, säiliön läpikäynti iteraattoreilla void nollaaAlkiot(vector<int>& vektori) { vector<int>::iterator i=vektori.begin(); //alkuun while (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; }

  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.

  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

  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

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

  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

  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>

  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.

  43. Vector esimerkki (1/3) 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). // //////////////////////////////////////////////////////////////////////

  44. Vector esimerkki (2/3) 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);

  45. Vector esimerkki (3/3) Program Output is: ===================== First element: 100 Last element: 400 Elements in vector: 4 After erasing 2 last element, new last element is: 200 After erasing first element, new first element is: 200 Elements in vector: 1

  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 //////////////////////////////////////////////////////////////////////

  47. Deque esimerkki (2/3) //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;

  48. Deque esimerkki (3/3) Program Output is: =============== a is empty a is not empty The contents of a : A B C D The first element of a is A The first element of a is A The last element of a is D The last element of a is D The contents of a : B C D

  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 // //////////////////////////////////////////////////////////////////////

  50. list esimerkki (2/3) // 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; } #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());

More Related