C++ - perusteet Java-osaajille luento 5/7: operaattoreiden ylikuormitus, oliotaulukko, parametrien oletusarvot, komentoriviparametrit, constant, inline, Operaattoreiden ylikuormitus Operaattoreiden kuormitus Operaattoreiden kuormituksella voidaan saada omat tietotyypit toimimaan perustyyppien tapaan: olio1 = olio2 + olio3; cout << olio1; olio[10] = 10; Operaattoreiden kuormitus Suoritusjärjestystä ei voi muuttaa Omia operaattoreita ei voi määritellä Alkuperäistä semantiikkaa ei kannata muuttaa Kuormitettavalla operaattorilla on oltava ainakin yksi operandi, joka on ohjelmoijan omaa tyyppiä Kuormitus tehdään kirjoittamalla globaali funktio tai jäsenfunktio Operaattoreista Operaattorit voidaan jakaa yksi ja kaksioperandisiin Yksioperandisia operaattoreita *olio -olio olio++ Kaksioperandisia operaattoreita olio1 + olio2 olio1 * olio2 olio1[10] cout << olio1 Kuormituksesta Sama operaattori voidaan yleensä toteutaa globaalina funktiona tai jäsenfunktiona Kuormitus vain jäsenfunktiona: = () [ ] -> Ei voida kuormittaa jäsen-operaattori:. nimiavaruus: :: ehdollinen sijoitus:?: 1
+ operaattori, järjestelmän tulkintatavat Kaksioperandisen + -operaattorin kutsu: olio1 + olio2 Järjestelmän tulkintatavat 1. Jäsenfunktio: olio1.operator+(olio2) 2. Globaali funktio: operator+(olio1,olio2) + -operaattori voidaan yleensä kuormittaa vaihtoehtoisesti globaalina funktiona tai jäsenfunktiona Esimerkki: plusoperaattori1.cpp, plusoperaattori2.cpp Sisältöoperaattori Yksioperandisen * -operaattorin kutsu: *olio Järjestelmän tulkintatavat Jäsenfunktio: olio.operator*() Globaali funktio: operator*(olio) * -operaattori voidaan kuormittaa vaihtoehtoisesti globaalina funktiona tai jäsenfunktiona Esimerkki: sisaltooperaattori.cpp Jälki- ja esilisäysoperaattorit Merkitys: Yhdellä kasvattaminen Jälkilisäys olio++ Saa lausekkeessa arvon ennen lisäystä Esilisäys ++olio Saa lausekkeessa arvon lisäyksen jälkeen Jälkilisäysoperaattorin kuormitus Globaalina Luokka operator++(luokka& olio, int); Jäsenfunktiona Luokka Luokka::operator++(int); Kokonaislukuparametria ei käytetä mihinkään! Se erottaa operaattorin esilisäyksestä, joka on saman niminen funktio (operator++) Esilisäysoperaattorin kuormitus Globaalina Luokka operator++(luokka& olio); Jäsenfunktiona Luokka Luokka::operator++(); Esimerkki: lisaykset.cpp Tulostusoperaattorista (<<) Tulostusoperaattori toteutetaan usein omille oliotyypeille << tulostaa siis mitä tahansa tietoa jolle operaattori on toteutettu << toimii useille eri tulostusvuotyypeille konsoli, tiedosto, muisti Käyttö: cout << olio1; Tulostuksia voi ketjuttaa: cout << Oliot: << olio1 <<, << olio2; 2
Tulostusoperaattorin kuormituksesta Jotta operaattori toimisi niin kuin pitää Käytettävä abstraktia tulostus stream tyyppiä ostream Ketjutus saadaan aikaan palauttamalla operaattorista viittaus tulostusvuohon Esimerkki: tulostusoperaattori.cpp Harjoitus Tee luokka ostos, joka pitää sisällään tietojäsenet string tuote (tuotteen nimi) ja int maara (tuotteen kpl määrä). Tee konstruktori, get ja set metodit. + ja - operaattori Ostos a("lapin kulta", 1); Ostos b("lapin kulta", 2); Ostos c = a + b // ("Lapinkulta", 3). Jos tuotteet eroavat, palautetaan null. Ostos d = b a // ("Lapinkulta, 1) <<operaattori cout<<c; //tulostaa: "Lapinkulta, 3kpl" [ ] operaattori cout<<c[1]; //tulostaa "Lapin kulta" cout<<c[2]; //tulostaa "3 kpl" Taulukko Oliotaulukko Taulukon voi luoda monella tapaa. Mitä tarkoittavat seuraavat rivit? 1. int taulukko[10]; 2. int *taulukko[10]; 3. int *taulukko = new int[n]; 4. int **taulukko =new int*[n]; Automaattinen oliotaulukko Olioita voidaan luoda useampia kerrallaan oliotaulukon avulla. Automaattinen oliotaulukko: Alkiot automaattisia: Kissa taulu[10]; Kissa a; taulu[0] = a; Alkiot dynaamisia: Kissa *taulu[10]; Kissa *a = new Kissa(); taulu[0] = a; Alkiot on tuhottava (silmukassa) delete-operaattorilla! Dynaaminen oliotaulukko Dynaaminen oliotaulukko: Alkiot automaattisia: Kissa *taulu = new Kissa[n]; Kissa a; taulu[0] = a; Taulukon tuhoaminen: delete [ ] taulu; Alkiot dynaamisia: Kissa **taulu = new Kissa*[lkm]; Kissa *a = new Kissa(); taulu[0] = a; Alkiot on tuhottava (silmukassa) delete-operaattorilla ja taulukko delete [ ] taulu; lauseella. 3
Komentoriviparametrit Komentoriviparametrit Mikäli halutaan lukea komentoriville annetut parametrit (tai argumentit) on pääohjelman esittely oltava seuraava: int main(int argc, char *argv[ ]) missä argc on komentoriviparametrien lukumäärä ja argv on taulukko, jonka alkioihin parametrit on talletettu C-tyylisinä merkkijonoina. Ensimmäisessä alkiossa on suoritetun ohjelman nimi ja muissa varsinaiset komentoriviparametrit. Komentoriviparametreja käytetään usein tiedostojen niminen tai ohjelman toimintaan vaikuttavien optioiden välitykseen. Esimerkki: komentoriviparametri.cpp Parametrien oletusarvot Parametrien oletusarvot Metodille (tai aliohjelmalle) voidaan välittää valinnainen määrä parametreja, kun parametreilla on oletusarvoja. Kutsuja voi valintansa mukaan jättää välittämättä parametrit, joilla on oletusarvot. Parametrien oletusarvot eivät kuulu C-kieleen. Oletusarvon määrittely: tyyppi metodi(tyyppi1 parametri1 = arvo1, tyyppi2 parametri2 = arvo2,, tyyppin parametrin = arvon); missä kaikilla parametreilla on oletusarvo Esim. void tulosta(int lkm = 3, bool vali = false); Parametrien oletusarvot Oletusarvot voidaan määritellä myös oletusarvottomien parametrien jälkeen: tyyppi metodi(tyyppi1 parametri1,, tyyppim parametrim = arvom+1, tyyppim+1 parametrim+1 = arvom+1,, tyyppin parametrin = arvon); missä parametrit 1 M ovat vapaita ja parametreille M+1 N on kiinnitetty oletusarvot. Esim. void tulosta(int lkm, bool vali = false); Parametrien oletusarvoja ei toisteta metodin toteutuksen yhteydessä: Esim. double nelio(int x, bool tulosta) Esimerkki: parametrienoletusarvot.cpp, parametrienoletusarvot2.cpp, Constant objects 4
Vakio-olio Varattua sanaa const voidaan käyttää myös olioiden yhteydessä. const clock rolex; Nyt rolex-oliolta voidaan kutsua ainoastaan niitä metodeja, jotka eivät vaikuta olion tietojäsenten arvoon. rolex.set(13,2,0); // int h, m, s. => virhe! Kaikki olion metodit eivät luonnollisestikaan vaikuta olion tietojäsenten arvoon. Nämä metodit ovat vakio-metodeja (constant methods) Vakiot: esimerkki class Clock int h, m, s; Clock(); Clock(int hour, int min, int sec); void set(int hour, int min, int sec); int read_hour(); int read_min(); int read_sec(); void write(); void tick(); ; Vakio-olio Virhe syntyy vaikka kutsuttaisiinkin metodia, joka ei vaikuta tietojäsenten arvoon: const clock rolex; rolex.read_hour(); // Virhe! Ne metodit, jotka eivät vaikuta tietojäsenten arvioihin, pitää määritellä erikseen const-määreellä. Tämä tapahtuu siten, että metodin perään kirjoitetaan sana const. Vakiot: esimerkki const Clock rolex; rolex.read_hour(); // OK! class Clock int h, m, s; Clock(); Clock(int hour, int min, int sec); void set(int hour, int min, int sec); int read_hour() const; int read_min() const; int read_sec() const; void write() const; void tick(); ; Vakiot: esimerkki Luonnollisesti sana const sisällytetään myös luokan toteutukseen: void Clock::write() const // tulosta aika Mitä kääntäjä sanoo seuraavasta? void Clock::write() const h = 0; // jossa h on tietojäsen // tulosta aika Hyvään tapaan kuuluu määritellä ne metodit vakioiksi, jotka eivät vaikuta tietojäsenten arvoon. Inline 5
inline-aliohjelmat Esikääntäjä korvaa inline-aliohjelmien kutsut aliohjelman rungolla. Esittely: inline tyyppi aliohjelma(parametrit); inline-aliohjelmilla voidaan optimoida ohjelman suoritusnopeutta, koska varsinaista siirtymistä aliohjelmaan ei tapahdu. inline-optimointi kuitenkin tärkeää vain hyvin aikakriittisissä tehtävissä. Vastaavasti ohjelmatiedoston koko kasvaa, koska aliohjelman sisältö kopioidaan jokaiseen kutsukohtaan. inline-aliohjelmat inline-aliohjelmien määrittely on kirjoitettava ennen sitä kutsuvien aliohjelmia, jotta järjestelmä pystyy kopioimaan rungot oikeisiin paikkoihin. Ei voida aina käyttää (esim. rekursiiviset aliohjelmat). Myöskään kääntäjät eivät aina pura inline-aliohjelmia. inline-aliohjelmat käytössä vain C++-kielessä. inline-aliohjelmat (esimerkki) Inline-funktiot 1 #include<iostream> 2 using namespace std; 3 inline int nelio(int a) 4 5 return a*a; 6 7 int main() 8 9 int a = 10; 10 cout << nelio(a); 11 return 0; 12 Rivi 10: Kääntäjä sisällyttää nelio-aliohjelman rungon tähän. Funktioiden toteutus on mahdollista kirjoittaa inline muodossa Funktion toteutus luokan esittelyn yhteyteen TAI Funktion toteutukseen inline sana Inline-funktiot class Luokka Luokka(int x) this->x = x; int x; ; class Luokka inline Luokka(int x); int x; ; //Toteutus otsikkotiedostoon: inline Luokka::Luokka(int x) this->x = x; 6