Geneeriset luokat. C++ - perusteet Java-osaajille luento 6/7: Template, tyyppi-informaatio, nimiavaruudet. Geneerisen luokan käyttö.

Samankaltaiset tiedostot
12 Mallit (Templates)

815338A Ohjelmointikielten periaatteet Harjoitus 5 Vastaukset

Operaattoreiden ylikuormitus. Operaattoreiden kuormitus. Operaattoreiden kuormitus. Operaattoreista. Kuormituksesta

Olio-ohjelmointi Syntaksikokoelma

15. Ohjelmoinnin tekniikkaa 15.1

C++11 Syntaksi. Jari-Pekka Voutilainen Jari-Pekka Voutilainen: C++11 Syntaksi

15. Ohjelmoinnin tekniikkaa 15.1

Ohjelmointi 1 Taulukot ja merkkijonot

Ohjelmointi 2. Jussi Pohjolainen. TAMK» Tieto- ja viestintäteknologia , Jussi Pohjolainen TAMPEREEN AMMATTIKORKEAKOULU

815338A Ohjelmointikielten periaatteet Harjoitus 3 vastaukset

Sisällys. 18. Abstraktit tietotyypit. Johdanto. Johdanto

Harjoitus Olkoon olemassa luokat Lintu ja Pelikaani seuraavasti:

18. Abstraktit tietotyypit 18.1

A TIETORAKENTEET JA ALGORITMIT

C++ rautaisannos. Kolme tapaa sanoa, että tulostukseen käytetään standardikirjaston iostreamosassa määriteltyä, nimiavaruuden std oliota cout:

Luokassa määriteltävät jäsenet ovat pääasiassa tietojäseniä tai aliohjelmajäseniä. Luokan määrittelyyn liittyvät varatut sanat:

12. Monimuotoisuus 12.1

ITKP102 Ohjelmointi 1 (6 op)

Virtuaalifunktiot ja polymorfismi

Javan perusteita. Janne Käki

812347A Olio-ohjelmointi, 2015 syksy 2. vsk. V Geneerisyys

Java-kielen perusteet

T Olio-ohjelmointi Osa 5: Periytyminen ja polymorfismi Jukka Jauhiainen OAMK Tekniikan yksikkö 2010

7. Näytölle tulostaminen 7.1

Olio-ohjelmointi 2. välikoe HYV5SN

Osoitin ja viittaus C++:ssa

Taulukot. Jukka Harju, Jukka Juslin

Tietueet. Tietueiden määrittely

Sisällys. Yleistä attribuuteista. Näkyvyys luokan sisällä. Tiedonkätkentä. Aksessorit. 4.2

Kääntäjän virheilmoituksia

13 Operaattoreiden ylimäärittelyjä

Java-kielen perusteet

JAVA-PERUSTEET. JAVA-OHJELMOINTI 3op A JAVAN PERUSTEET LYHYT KERTAUS JAVAN OMINAISUUKSISTA JAVAN OMINAISUUKSIA. Java vs. C++?

ITKP102 Ohjelmointi 1 (6 op)

Alkuarvot ja tyyppimuunnokset (1/5) Alkuarvot ja tyyppimuunnokset (2/5) Alkuarvot ja tyyppimuunnokset (3/5)

Metodit. Metodien määrittely. Metodin parametrit ja paluuarvo. Metodien suorittaminen eli kutsuminen. Metodien kuormittaminen

12. Monimuotoisuus 12.1

on ohjelmoijan itse tekemä tietotyyppi, joka kuvaa käsitettä

Olio-ohjelmointi Javalla

TIETORAKENTEET JA ALGORITMIT

Sisällys. Yleistä attribuuteista. Näkyvyys luokan sisällä ja ulkopuolelta. Attribuuttien arvojen käsittely aksessoreilla. 4.2

Sisällys. 1. Omat operaatiot. Yleistä operaatioista. Yleistä operaatioista

C# olio-ohjelmointi perusopas

Ohjelmointitaito (ict1td002, 12 op) Kevät Java-ohjelmoinnin alkeita. Tietokoneohjelma. Raine Kauppinen

ITKP102 Ohjelmointi 1 (6 op)

16. Javan omat luokat 16.1

Opintojakso TT00AA11 Ohjelmoinnin jatko (Java): 3 op Taulukot & Periytyminen

2. Lisää Java-ohjelmoinnin alkeita. Muuttuja ja viittausmuuttuja (1/4) Muuttuja ja viittausmuuttuja (2/4)

Algoritmit 1. Luento 3 Ti Timo Männikkö

T Olio-ohjelmointi Osa 3: Luokka, muodostin ja hajotin, this-osoitin Jukka Jauhiainen OAMK Tekniikan yksikkö 2010

Olion elinikä. Olion luominen. Olion tuhoutuminen. Olion tuhoutuminen. Kissa rontti = null; rontti = new Kissa();

Olio-ohjelmoinnissa luokat voidaan järjestää siten, että ne pystyvät jakamaan yhteisiä tietoja ja aliohjelmia.

4. Luokan testaus ja käyttö olion kautta 4.1

Mallit standardi mallikirjasto parametroitu tyyppi

Pino S on abstrakti tietotyyppi, jolla on ainakin perusmetodit:

Sisällys. 9. Periytyminen Javassa. Periytymismekanismi Java-kielessä. Periytymismekanismi Java-kielessä

9. Periytyminen Javassa 9.1

1. Omat operaatiot 1.1

812341A Olio-ohjelmointi Peruskäsitteet jatkoa

Muuttujien roolit Kiintoarvo cin >> r;

Kääreluokat (oppikirjan luku 9.4) (Wrapper-classes)

9. Periytyminen Javassa 9.1

Java kahdessa tunnissa. Jyry Suvilehto

Osa VII. Mitä mallit ovat ja kuinka niitä käytetään Miksi mallit tarjoavat paremman vaihtoehdon makroille Kuinka luokkamalleja luodaan

Ohjelmassa henkilön etunimi ja sukunimi luetaan kahteen muuttujaan seuraavasti:

Taulukot. Taulukon määrittely ja käyttö. Taulukko metodin parametrina. Taulukon sisällön kopiointi toiseen taulukkoon. Taulukon lajittelu

Demo 6 vastauksia. 1. tehtävä. #ifndef #define D6T1 H D6T1 H. #include <iostream> using std::ostream; using std::cout; using std::endl;

TIE448 Kääntäjätekniikka, syksy Antti-Juhani Kaijanaho. 27. lokakuuta 2009

Sisällys. 9. Periytyminen Javassa. Periytymismekanismi Java-kielessä. Periytymismekanismi Java-kielessä

812336A C++ -kielen perusteet,

Kielioppia: toisin kuin Javassa

Ohjelmointi 2 / 2010 Välikoe / 26.3

Opintojakso TT00AA11 Ohjelmoinnin jatko (Java): 3 op. Standardi- ja tietorakenneluokkia

Ohjelmointi 2 / 2011 Välikoe / 25.3

AS C-ohjelmoinnin peruskurssi 2013: C-kieli käytännössä ja erot Pythoniin

Lyhyt kertaus osoittimista

1 Tehtävän kuvaus ja analysointi

Informaatioteknologian laitos Olio-ohjelmoinnin perusteet / Salo

Rajapinta (interface)

\+jokin merkki tarkoittaa erikoismerkkiä; \n = uusi rivi.

Ohjelman virheet ja poikkeusten käsittely

Ohjelmassa muuttujalla on nimi ja arvo. Kääntäjä ja linkkeri varaavat muistilohkon, jonne muuttujan arvo talletetaan.

Olio-ohjelmointi Geneerisyys. 1. Johdanto

Ohjelmointitaito (ict1td002, 12 op) Kevät Java-ohjelmoinnin alkeita. Tietokoneohjelma. Raine Kauppinen

KOHDELUOKAN MÄÄRITTELY

812347A Olio-ohjelmointi, 2015 syksy 2. vsk. X Poikkeusten käsittelystä

Kompositio. Mikä komposition on? Kompositio vs. yhteyssuhde Kompositio Javalla Konstruktorit set-ja get-metodit tostring-metodi Pääohjelma

C++11 lambdat: [](){} Matti Rintala

Tietorakenteet ja algoritmit

7/20: Paketti kasassa ensimmäistä kertaa

Sisällys. Metodien kuormittaminen. Luokkametodit ja -attribuutit. Rakentajat. Metodien ja muun luokan sisällön järjestäminen. 6.2

Ohjelmointiharjoituksia Arduino-ympäristössä

Sisällys. JAVA-OHJELMOINTI Osa 7: Abstrakti luokka ja rajapinta. Abstraktin luokan idea. Abstrakti luokka ja metodi. Esimerkki

Tietorakenteet. JAVA-OHJELMOINTI Osa 5: Tietorakenteita. Sisällys. Merkkijonot (String) Luokka String. Metodeja (public)

Apuja ohjelmointiin» Yleisiä virheitä

Luokka Murtoluku uudelleen. Kirjoitetaan luokka Murtoluku uudelleen niin, että murtolukujen sieventäminen on mahdollista.

Sisällys. JAVA-OHJELMOINTI Osa 6: Periytyminen ja näkyvyys. Luokkahierarkia. Periytyminen (inheritance)

Rajapinnasta ei voida muodostaa olioita. Voidaan käyttää tunnuksen tyyppinä. Rajapinta on kuitenkin abstraktia luokkaa selvästi abstraktimpi tyyppi.

Operaattoreiden uudelleenmäärittely

Opintojakso TT00AA11 Ohjelmoinnin jatko (Java): 3 op. Tietorakenneluokkia 2: HashMap, TreeMap

Transkriptio:

Geneeriset luokat C++ - perusteet Java-osaajille luento 6/7: Template, tyyppi-informaatio, nimiavaruudet Geneerinen luokka tarkoittaa parametroitua luokkamallia, jonka avulla voidaan muodostaa useita, tietyllä tavalla toimivia "oikeita" luokkia. Luokkaa, joka on luotu geneerisen luokan mukaan, voidaan kutsua luokkamallin ilmentymäksi. (Samaan tapaan kuin oliota kutsutaan luokan ilmentymäksi) Geneerisestä luokasta ei voida suoraan luoda olioilmentymää. Geneerisestä luokasta luodaan ensin oikeita luokkia ja niistä sitten olioilmentymiä. Geneerisen luokan käyttö Geneeriset luokat Geneerisen luokat ovat hyödyllisiä haluttaessa yleiskäyttöisiä säiliöluokkia. Tyypillisiä säiliöluokkia ovat perustietorakenteita (listat, pinot, puut) kuvaavat luokat. Jos haluamme Pino-luokan, joka pitää sisällään Luokan X-olioita, teemme geneerisen Pino-luokan, johon kiinnitetään käännösaikaisesti parametriksi X-luokan olio. Miten geneeriset luokat ovat toteutettu Javassa? Java vs. C++ geneerisyys Java Javassa jokainen luokka periytyy Object-luokasta. Tällöin geneeristen luokkien teko perustuu periytymiseen. Jos luokan tyyppinä on siis Object, edustaa tämä olio Javan mitä tahansa oliota. Jos perustietotyyppejä halutaan säilöä "geneerisesti", pitää Javassa luoda näistä olioilmentymiä (Integer, Double ). C++ Myös C++:ssa geneerisyyden voi toteuttaa periyttämällä. Tällöin tosin pitää luoda Javan kaltainen Object-luokka ja periyttää kaikki käyttämänsä luokat tästä. C++:ssan tapa toteuttaa geneeriset luokat on template-luokat. (Malli, kaavain) Template (mallit) Mallit mahdollistavat tyyppiriippumattoman ohjelmointitavan (geneerinen ohjelmointi) Mallista generoidaan tyyppiin sidottuja ohjelmakoodiversioita käännösaikana C++:ssa voidaan geneerisiä 1. funktioita 2. luokkia 1

Funktiomallit Aina samanlaisena toistuva aliohjelmalogiikka voidaan kirjoittaa aliohjelmamalliksi (function template). Kääntäjä generoi aliohjelman kutsun yhtyedessä käytetyyn tyyppiin sopivan aliohjelman. Ilman aliohjelmamallija on kirjoitettava useita eri aliohjelmia, joissa logiikka on sama, mutta tiedon tyyppi vaihtelee. Ei siis käytetä funktion kuormittamista, vaan käytetään aliohjelmamalleja. Tavallinen aliohjelma int min(int p_eka, int p_toka) if(p_eka < p_toka) return p_eka; return p_toka; double max(double p_eka, double p_toka) if(p_eka < p_toka) return p_eka; return p_toka; cout << min(10, 3 ) << endl; cout << min(10.0, 12.7) << endl; Aliohjelmamalli T min(t p_eka, T p_toka) if(p_eka < p_toka) return p_eka; return p_toka; cout << min(10, 0) << endl; cout << min(12.2, 12.7) << endl; Aliohjelmamalli Aliohjelman esittely ja määrittely: template <geneerinen_tyyppi> aliohjelman toteutus Geneerisen tyypin nimenä käytetään tavallisesti yhtä suurta kirjainta (usein T). < > - merkkien välissä voi olla useita eri geneerisiä tyyppitunnuksia pilkuilla erotettuina. Kunkin tunnuksen eteen kirjoitetaan varattu sana class Kääntäjä korvaa geneerisen tyypin T kutsussa käytetyn tiedon tyypillä. Luokkamalli Luokkamalli (class template) sisältää tarvittavat tietojäsenet ja geneeristä tietotyyppiä käsittelevät aliohjelmamallit. Luokkamallin avulla voidaan uudelleenkäyttää kaikkia luokan aliohjelmia kirjoittamatta niitä uudelleen. Luokkamallin tyypillisiä käyttökohteita ovat mm. tietorakenneluokat. Luokkamallin määrittely ja aliohjelmien toteutus kirjoitetaan samaan tiedostoon. Aliohjelmia ei sijoiteta erilliseen cpp-tiedostoon, koska ne eivät ole sellaisenaan käännettäviä Aliohjelmat voidaan kääntää vasta, kun kääntäjä on korvannut geneerisen tyypin käytetyllä tyypillä tai luokalla. Luokkamallin määrittely Luokkamallin määrittely template <geneerinen_tyyppi> class Malliluokka ; Metodeiden esittelyn yhteydessä ei tarvitse käyttää merkintää Geneerisestä luokasta luodaan "normaali"-luokka kertomalla sille tyyppi (siis class T:n oikea tyyppi) Geneerinen luokka: Malliluokka Tavallinen luokka: Malliluokka<int> Luokan ilmentymä: Malliluokka<int> ilmentymä 2

Geneerinen luokka, C++ template <class t> class GeneerinenPino..; GeneerinenPino<Henkilo> GeneerinenPino<Henkilo> henkilopino Luokkamalli, esimerkki: pino Tehdään edellisessä kuvassa ollut GeneerinenPino-luokka Pinoon voidaan siis laittaa mitä tahansa olioita ja primitiivityyppejä. Toteutetaan Pino käyttäen dynaamista taulukkoa (listaratkaisu luonnollisesti parempi) GeneerinenPino<Auto> GeneerinenPino<Auto> autopino Pino Pino Pino (stack) sisältää alkioita, joita lisätään ja poistetaan pinon huipulta LIFO-periaatteen mukaisesti (last-in-first-out). Perusoperaatiot ovat pinoa (push), joka lisää uuden alkion pinon päälle ja pura (pop), joka poistaa alkion pinon päältä. Muita operaatioita ovat koko: Palauttaa alkioiden lukumäärän. onkotyhja: Tarkistetaan onko pino tyhjä. huippu: Palauttaa päällimmäisen alkion poistamatta sitä pinosta. tulosta: Tulostetaan pinon sisältö. Sarja pino-operaatioita ja niiden vaikutus pinoon. Pino on aluksi tyhjä. Operaatio onkotyhja() pinoa(1) pinoa(2) huippu() pinoa(3) koko() pura() pura() onkotyhja Tulos 2, 3 3, 2 false Pino () (1) (2,1) (2,1) (3,2,1) (3,2,1) (2,1) (1) (1) Pino: tavallinen toteutus class Pino private: int *lista; int ylin; int maksimi; Pino(int koko); void pinoa(int x); int pura(); ; Pino: tavallinen toteutus Pino::Pino(int koko) lista = new int[koko]; maksimi = koko -1; ylin = -1; void Pino::pinoa(int x) if(ylin < maksimi) lista[++ylin] = x; int Pino::pura() if(ylin >= 0) return lista[ylin--]; 3

Pino: tavallinen toteutus Pino pino(10); pino.pinoa(2); pino.pinoa(3); cout << pino.pura(); // 3 cout << pino.pura(); // 2 Mitä virheitä edellisessä toteutuksessa oli? Pino: template-toteutus class GeneerinenPino private: T *lista; // dynaamisesti varattava taulukko; int ylin; int maksimi; GeneerinenPino(int koko); ~GeneerinenPino(); void pinoa(t x); T pura(); ; Pino: template-toteutus GeneerinenPino<T>::GeneerinenPino(int koko) lista = new T[koko]; maksimi = koko -1; ylin = -1; GeneerinenPino<T>::~GeneerinenPino() delete lista; void GeneerinenPino<T>::pinoa(T x) if(ylin < maksimi) lista[++ylin] = x; T GeneerinenPino<T>::pura() if(ylin >= 0) return lista[ylin--]; Pino: template-toteutus GeneerinenPino<int> intpino(2); intpino.pinoa(2); intpino.pinoa(3); cout << intpino.pura(); cout << intpino.pura(); GeneerinenPino<string> stringpino(3); stringpino.pinoa("hellurei"); stringpino.pinoa("xxx"); stringpino.pinoa("yyy"); cout << stringpino.pura() << endl; cout << stringpino.pura() << endl; Harjoitus: funktiomalli a) Kirjoita funktiomalli samat(), joka palauttaa totuusarvon, jos kaksi sille välitettyä parametria (tyyppi mikä tahansa) ovat samat. Aliohjelma palauttaa arvon false, jos parametrit eivät ole samat. Testaa kokonaisluvuilla ja liukuluvuilla b) Tee luokka Henkilo, jolla on tietojäsenenä nimi ja henkilötunnus (string). Tee konstruktori ja get ja set metodit tietojäseniä varten. Luo main-funktiossa kaksi Henkilö oliota (Älä käytä dynaamista muistinvarausta). Välitä oliot funktiolla samat. Miten Henkilo-luokkaa pitää muuttaa, jotta funktio toimisi? Toteuta ratkaisusi siten, että Henkilo-oliot ovat samat, kun olioiden tietojäsenet (nimi ja henk. tunnus) ovat samat. Harjoitus: luokkamalli a) Kirjoita luokkamalli Pari, jonka jäseniksi voidaan tallettaa kaksi minkä tahansa tyyppistä tietoa/olioita. Määrittele luokkaan muodostin, joka saa nämä kaksi oliota parametrina. Tee luokalle tulosta-funktio, joka tulostaa tietojäsenet (cout). Testaa kokonaisluvuilla ja liukuluvuilla b) Testaa luokkamallia Henkilo-luokan olioilla. Kun tulostafunktiota kutsutaan, tulostetaan henkilöiden nimi ja henkilötunnus. (Kuormita operaattori <<) 4

Tyyppi-informaatio Tyyppi-informaatio ja tyyppimuunnokset RTTI (run-time type information)-mekanismi mahdollistaa olioiden tyypin ajonaikaisen tutkimisen. RTTI:n avulla on mahdollista tutkia olioiden luokkatyyppejä ja periytymishierarkiaa. Uudehko C++-mekanismi Vanhat kääntäjät eivät tue Visual C++ 6.0 ei oletusarvoisesti tue projektiasetuksista tuen saa päälle RTTI, typeid typeid-operaattorilla voidaan tutkia olion luokka. Operaattori palauttaa viittauksen Type_info olioon, joka edustaa olion tyyppinimeä. Operaattorin käyttö: typeid(lauseke) lauseke on osoitin tai viittaus olioon, muuttujanimi tai tyyppinimi Type_info luokkaa on ylikuormitettu operaattorit == ja!=, joilla voidaan vertailla lausekkeiden tyyppien yhdenmukaisuutta. before aliohjelmalla voidaan tarkistaa luokkajärjestystä typeid, yksinkertainen esimerkki #include <typeinfo> using namespace std; int x = 2; cout << typeid(x).name() << '\n'; // Tyypin nimi if(typeid(x) == typeid(4)) // Tyyppien vertailu cout << "Kummatkin kokonaislukuja\n"; typeid: monimutkainen esimerkki class Ylaluokka int x, y; virtual void f()cout << "\nyläluokka"; virtual void f1() ; class Alaluokka: Ylaluokka int x; void f()cout <<"\nalaalaluokka"; ; typeid: monimutkainen esimerkki Ylaluokka a; Alaluokka *c = new Alaluokka(); cout << "\na-olion luokan nimi: "; cout << typeid(a).name(); cout << "\nc-olion luokan nimi: "; cout << typeid(*c).name(); if(typeid(a) == typeid(*c)) cout <<"\na ja c ovat saman luokan olioita"; cout <<"\na ja c eivät ole saman luokan olioita"; if(typeid(ylaluokka).before(typeid(alaluokka))) cout << "\nalaluokka on Yläluokan yläpuolella"; if(typeid(*c).before(typeid(a))) cout << "\nc-olion luokka a-olion luokan yläpuolella\n"; 5

Tyyppimuunnoksista C-kielen tyyppimuunnokset sekoittuvat helposti muuhun ohjelmakoodiin. C++-kieleen on rakennetty tyyppimuunnos-operaattoreita static_cast const_cast dynamic_cast static_cast static_cast-operaattori muuntaa parametrina saadun muuttujan tai olion tyyppiä. Operaattorin toiminta vastaa C-kielen tyyppimuunnoksen toimintaa, mutta on näkyvämpi koodissa. int luku = 10; double tulos; tulos = static_cast<double>(luku) / 3; const_cast const_cast-operaattori poistaa parametrista const määreen. Muutoksen jälkeen muuttujaan voi siis tehdä muutoksia. int countchars(char *pstring, char character) // Teejotain return 1; const char *p_somestring = "Let's make things better"; int result = countchars(const_cast<char *> (p_somestring), 'e'); RTTI, dynamic_cast<t> T* osoitin2 = dynamic_cast<t*>(osoitin1); Turvallinen tyyppimuunnos luokkahierarkiassa Palauttaa NULL, jos muunnos ei onnistu Mahdollistaa osajoukon valinnan Esim. Valitaan henkilöistä pelkät työntekijät Esimerkki: dynamic_cast Tyontekija tyontekija("kalle","kojootti","kalastaja"); Henkilo *h = &tyontekija; Tyontekija *t; t = dynamic_cast<tyontekija*>(h); if(t) // jos kyseessä työntekijä cout << "Titteli: " << t->gettitteli() << endl; Nimiavaruudet 6

Nimiavaruudet Nimiavaruuden käyttö Yksitasoisen globaalin avaruuden ongelma Saman nimisiä globaaleja symboleja ei saa olla Yksitasoinen nimiavaruus ongelma erityisesti suurissa projekteissa, joissa ohjelmakoodi tulee useilta eri toimittajilta Perinteinen ratkaisu: Etuliitteen käyttö C++-kielen ratkaisu: Nimiavaruudet Javan ratkaisu: Paketti Tapa 1, nimiavaruus käyttöön using namespace std; void main() cout << Terve! << endl; Tapa 2, tunnuksen kautta viittaaminen void main() std::cout << Terve! << std::endl; Oman nimiavaruuden määrittely Nimiavaruus: esimerkki Määrittely jokaiseen otsikko/lähdekooditiedostoon // omat.h namespace oma class Luokka... ; void funktio(); Oman avaruuden käyttö #include omat.h void main() oma::luokka olio; oma::funktio(); #include <string> namespace a void tervehdi(std::string a) std::cout << a; namespace b std::string tervehdys = "moi"; a::tervehdi(b::tervehdys); 7