C++ -ohjelmointikieli vs. muut kielet C C++ C vs. C++: Hello World. C++ -ohjelman kääntämisen vaiheet kuin C-kieli

Samankaltaiset tiedostot
C++ -ohjelmointikieli vs. muut kielet

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

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

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

13 Operaattoreiden ylimäärittelyjä

Java-kielen perusteet

Ohjelmointi 1 Taulukot ja merkkijonot

Osoitin ja viittaus C++:ssa

815338A Ohjelmointikielten periaatteet Harjoitus 3 vastaukset

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

Tiedostot. Tiedostot. Tiedostot. Tiedostot. Tiedostot. Tiedostot

815338A Ohjelmointikielten periaatteet Harjoitus 5 Vastaukset

Merkkijono määritellään kuten muutkin taulukot, mutta tilaa on varattava yksi ylimääräinen paikka lopetusmerkille:

Tietueet. Tietueiden määrittely

ITKP102 Ohjelmointi 1 (6 op)

Olio-ohjelmointi Syntaksikokoelma

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

Harjoitustyö: virtuaalikone

Java-kielen perusteet

Olio-ohjelmointi Javalla

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

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

7. Näytölle tulostaminen 7.1

Sisällys. 7. Oliot ja viitteet. Olion luominen. Olio Java-kielessä

7. Oliot ja viitteet 7.1

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

Sisältö. 2. Taulukot. Yleistä. Yleistä

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

Lyhyt kertaus osoittimista

VIII. Osa. Liitteet. Liitteet Suoritusjärjestys Varatut sanat Binääri- ja heksamuoto

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

Yleistä. Nyt käsitellään vain taulukko (array), joka on saman tyyppisten muuttujien eli alkioiden (element) kokoelma.

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

1. Omat operaatiot 1.1

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

Perusteet. Pasi Sarolahti Aalto University School of Electrical Engineering. C-ohjelmointi Kevät Pasi Sarolahti

C-kielessä taulukko on joukko peräkkäisiä muistipaikkoja, jotka kaikki pystyvät tallettamaan samaa tyyppiä olevaa tietoa.

ITKP102 Ohjelmointi 1 (6 op)

12 Mallit (Templates)

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

Ohjelmointiharjoituksia Arduino-ympäristössä

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

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

Perusteet. Pasi Sarolahti Aalto University School of Electrical Engineering. C-ohjelmointi Kevät Pasi Sarolahti

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

Moduli 4: Moniulotteiset taulukot & Bittioperaatiot

Kääntäjän virheilmoituksia

Koottu lause; { ja } -merkkien väliin kirjoitetut lauseet muodostavat lohkon, jonka sisällä lauseet suoritetaan peräkkäin.

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

Sisällys. 12. Näppäimistöltä lukeminen. Yleistä. Yleistä

Mallit standardi mallikirjasto parametroitu tyyppi

Ohjelman virheet ja poikkeusten käsittely

815338A Ohjelmointikielten periaatteet Harjoitus 2 vastaukset

12. Näppäimistöltä lukeminen 12.1

Rakenteiset tietotyypit Moniulotteiset taulukot

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

Muistin käyttö. Muistin käyttö. Muistin käyttö. Muistin käyttö. Muistin käyttö. Muistin käyttö. Muistin käyttö C-ohjelmassa

Muuttujien roolit Kiintoarvo cin >> r;

ITKP102 Ohjelmointi 1 (6 op)

Loppukurssin järjestelyt

Sisältö. 22. Taulukot. Yleistä. Yleistä

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

Ohjelmoinnin perusteet Y Python

Taulukot. Jukka Harju, Jukka Juslin

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

11. Javan toistorakenteet 11.1

Java-kielen perusteet

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

TTY Ohjelmointi I & II C++-kirjastoreferenssi versio 2.2

Ohjelmoinnin jatkokurssi, kurssikoe

15. Ohjelmoinnin tekniikkaa 15.1

Osoittimet ja taulukot

15. Ohjelmoinnin tekniikkaa 15.1

Osoittimet ja taulukot

12. Monimuotoisuus 12.1

Chapel. TIE Ryhmä 91. Joonas Eloranta Lari Valtonen

Loppukurssin järjestelyt C:n edistyneet piirteet

Ohjelmoinnin perusteet Y Python

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

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

Harjoitus Olkoon olemassa luokat Lintu ja Pelikaani seuraavasti:

Javan perusteita. Janne Käki

ITKP102 Ohjelmointi 1 (6 op)

Osoittimet. Mikä on osoitin?

8. Näppäimistöltä lukeminen 8.1

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

Tietorakenteet ja algoritmit

Apuja ohjelmointiin» Yleisiä virheitä

C-ohjelmointikieli vs. muut kielet

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

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

12. Javan toistorakenteet 12.1

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

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

Binäärioperaatiot Tiedostot ja I/O

Tietotyypit ja operaattorit

C-ohjelmointikieli vs. muut kielet. C vs. Java ("stereotyyppisesti") C-ohjelman korkean tason rakenne. Java-ohjelman korkean tason rakenne

Pong-peli, vaihe Koordinaatistosta. Muilla kielillä: English Suomi. Tämä on Pong-pelin tutoriaalin osa 2/7. Tämän vaiheen aikana

17. Javan omat luokat 17.1

Tietojen syöttäminen ohjelmalle. Tietojen syöttäminen ohjelmalle Scanner-luokan avulla

Transkriptio:

C++ -ohjelmointikieli vs. muut kielet C++-kieli ohjelmointikielten sukupuussa (C++ -kielen kehittäjää Bjarne Stroustrupia mukaillen): C++ -ohjelman kääntämisen vaiheet kuin C-kieli C C++ C++: kehittäjänsä Bjarne Stroustrupin mukaan C++ is a better C. Pohjana C-kieli, joka on pidetty mukana lähes täysin sellaisenaan. Monet C-ohjelmat ovat sellaisenaan myös laillisia C++ -ohjelmia. C-kielen perusominaisuuksia täydennetty lisäämällä esimerkiksi: Nimiavaruudet. Viitteet. Luokat (sekä niihin liittyviä olio-ohjelmoinnin mekanismeja). Poikkeukset. Kaavaimet. STL-kirjasto (Standard Template Library). Sisältää valmiita säiliöluokkia sekä apufunktioita niiden käsittelyyn. Edellisiä (ja toki muutakin) käydään läpi kurssin edetessä. 1 C vs. C++: Hello World ANSI C (esim. tiedostossa hello.c ): #include <stdio.h> int main(void) /* Hello World C-kielen tapaan. */ { printf("hei maailma!\n"); return 0; Kääntö (ilman erillistä nimeä): gcc hello.c Suoritus:./a.out (Linux/OS X), a tai a.exe (Windows) Huom: tämä on sinänsä laillinen myös C++ -ohjelmana! C++ (esim. tiedostossa hello.cpp ): #include <iostream> int main() // Hello World C++ -kielen tapaan. { std::cout << "Hei maailma!\n"; Kääntö ilman erillistä nimeä: g++ hello.cpp Suoritus:./a.out (Linux/OS X), a tai a.exe (Windows) 4 3 2

C++:n Hello World C++-kielisen Hello World -ohjelman erot ANSI C-kielen versioon: #include <iostream> int main(){ // Hello World C++ -kielen tapaan. std::cout << "Hei maailma!\n"; Otsaketiedosto iostream sisältää C++:n luku- ja tulostustoimintoja. C++-kielessä voi kommentoida käyttäen kahta kauttamerkkiä //. Muoto main() vastaa parametritonta funktiota main(void). Tulostus tehdään tulostusolion cout kautta käyttäen << -operaattoria. << toimii tässä tulostusoperaattorina : ohjaa oikealla puolellaan annetun datan vasemmalla puolellaan olevalle tulostusoliolle. cout on C++ -kielen vastine C-kielen stdout:lle. Tulostaa standarditulostevirtaan (eli tyypillisesti ruudulle). Käytetään nimiavaruutta: cout-olio sijaitsee nimiavaruuden std sisällä. main-funktio palauttaa oletuksena arvon 0, jos return-lause puuttuu. Nimiavaruudet... #include <iostream> // Tulostetaan iostreamin cout-oliolla. namespace eka { void tulosta() { std::cout << "Olen eka:ssa\n"; namespace toka { void tulosta() { std::cout << "Olen toka:ssa\n"; namespace eka { int x; // Samaa nimiavaruutta voi täydentää osissa. // Muuttuja eka::x. void tulosta() { std::cout << "Olen globaalissa nimiavaruudessa\n"; tulosta(); // Kutsuu globaalia funktiota tulosta. eka::tulosta(); // Kutsuu nimiavaruuden eka funktiota tulosta. toka::tulosta(); // Kutsuu nimiavaruuden toka funktiota tulosta. ::tulosta(); // Eksplisiittinen viittaus globaaliin tulosta. Nimiavaruudet Nimiavaruus: mahdollistaa keskenään samanlaisten nimien rinnakkaiselon koodissa. Jos kaksi samaa nimeä (esim. kaksi samannimistä muuttujaa) ovat eri nimiavaruuksissa, eivät niiden nimet törmää keskenään. Vrt. Java: kaksi samannimistä luokkaa eivät törmää keskenään, jos ne ne sijaitsevat eri paketeissa. Kaikki C++-standardikirjaston sisältö on nimiavaruuden std sisällä. Nimiavaruuden sisään voidaan viitata resoluutio-operaattorilla :: std::cout on viittaus nimiavaruuden std sisällä olevaan nimeen cout. Nimiavaruuden sisältö määritellään avainsanalla namespace tapaan: namespace nimiavar_nimi {// Aaltosulkeiden sisältö kuuluu nimiavaruuteen nimiavar_nimi. int x; // Esim. tämä int-muuttuja on nimiavar_nimi::x. 5 Nimiavaruudet... void tulosta(int x) { std::cout << x << "\n"; //Huomaa ketjutus: ensin x, sitten "\n". namespace eka { const int x = 1; void tulosta() { ::tulosta(x); namespace toka { const int x = 2; void tulosta() { ::tulosta(x); namespace esimerkki { void tulosta() { ::tulosta(eka::x + toka::x); eka::tulosta(); // Tulostuu 1. toka::tulosta(); // Tulostuu 2. esimerkki::tulosta(); // Tulostuu 3. 8 7 6

Nimiavaruudet... Nimiavaruuksista voi tuoda sisältöä näkyviin using-direktiivin avulla: using namespace nimiavar; Tuo nimiavaruuden nimiavar sisällön kokonaisuudessaan näkyviin koodin nykyiseen näkyvyysalueeseen. Pitäisi käyttää harkiten: liiallinen käyttö kumoaa nimiavaruuksien alkuperäisen ajatuksen. using nimiavar::nimi; Tuo nimiavaruudesta nimiavar näkyviin ainoastaan nimen nimi. namespace esimerkki { int x; int y; using esimerkki::x; // Tuodaan esimerkki::x näkyviin. int a = x + esimerkki::y; // x näkyy suoraan, mutta y ei. using namespace esimerkki; int b = x + y; // Nyt sekä x että y näkyvät suoraan. Nimiavaruudet... Nimiavaruuksille voi luoda aliaksia: namespace toinen = nimiavar; Luo nimiavaruuden nimelle nimiavar aliaksen toinen. Voisi käyttää esim. eri koodiversioiden välillä valitsemiseen. namespace versio_1 { const int x = 100; namespace versio_2 { const int x = 200; namespace ver = versio_1; int a = ver::x; // a = 100 namespace ver = versio_2; int b = ver::x; // a = 200 9 10 Nimiavaruudet... Nimiavaruuksien erikoistapaus: nimetön nimiavaruus Luo nimiavaruuden, jonka sisältö näkyy vain kyseisessä tiedostossa. Eri tiedostoissa olevat nimettömät nimiavaruudet eivät törmää keskenään. C++:ssa static-määrettä suositeltavampi tapa piilottaa sisäisiä nimiä. namespace { // Ai anneta nimeä = nimetön nimiavaruus. const int x = 100; // Ei näy tiedoston ulkopuolelle. static int y = 200; // Sisäisesti linkittyvä: ei näy ulos. C-standardikirjasto ja nimiavaruudet: C++ -standardikirjasto sisältää C-kielen standardikirjaston otsaketiedostot uudelleen nimettyinä. Muotoa otsake.h oleva otsaketiedosto on nimetty muotoon cotsake. Esim. #include <cstdio> tuo C++ -kielen version tiedostosta stdio.h. Ero: uudelleen nimettyjen tiedostojen sisältö on nimiavaruudessa std. Yhteensopivuuden vuoksi myös nimiavaruudettomat tiedostot C++ -kielen varatut sanat (vs. C-kieli) Seuraavat nimet ovat C++ -kielessä varattuja (ei voi käyttää esim. muuttujien niminä): C-kielen varatut sanat on merkitty mustalla. Punaiset ovat C++ -kielen lisäämiä uusia varattuja sanoja. asm do if return typedef auto double inline short typeid bool dynamic_cast int signed typename break else long sizeof union case enum mutable static unsigned catch explicit namespace static_cast using char export new struct virtual class extern operator switch void const false private template volatile const_cast float protected this wchar_t continue for public throw while default friend register true delete goto reinterpret_cast try (esim. stdio.h) ovat olemassa, mutta niitä ei pidä enää käyttää! 11 12

C++ -kielen muutokset C-kieleen C-kieli on otettu lähes sellaisenaan C++ -kielen pohjaksi. Oleellisimmat erot C++ - ja C-kielen yhteisten ominaisuuksien välillä ovat: C++ -kielessä funktio, jolle määritetään tyhjä parametrilista, ei ota parametreja (on ns. void-funktio). Esim. int f() on int-arvon palauttava funktio f, joka ei ota parametreja. C-kielessähän pitäisi tarvittaessa antaa erikseen määre void: int f(void) on funktio, joka ei ota parametreja. int f() on funktio, joka ottaa mitä tahansa parametreja (ei määritetä). C++ -kielessä struct-tietutyypin nimeen voidaan viitata suoraan. C-kielessä mukana määre struct (ellei erillistä typedef-määritystä). struct Koordinaatti {int x, int y; int main(){ Koordinaatti a = {10, 20; struct Koordinaatti b = {-10, 5; // Ok C++ -kielessä. // C-kielen tapa. C++ -kielen muutokset C-kieleen... C++ ei salli void-osoittimen arvon asetusta muuntyyppiseen osoittimeen ilman erillistä tyyppimuunnosta. int main(){ char *mj = malloc(100); // C++: virhe. C: ok. char *mj2 = (char *) malloc(100); // C++: ok. C: ok. C++ ei salli ylimääräisiä taulukon alustusarvoja. char mj[3] = "C++"; // C++: '\0' ei mahdu! C++ -kielen muutokset C-kieleen... C++ -kielen enum-vakiotyypit hyväksyvät vain arvoja, jotka ovat sen kanssa samaa tyyppiä ja kuuluvat sen arvoalueeseen. enum Paiva {ma, ti, ke, to, pe, la, su; // Arvot 0-6. int main(){ Paiva a = ma; Paiva b = 3; // Ok: ma on Paiva-arvo. // Virhe: 3:n tyyppi on int eikä Paiva. C++ -kielessä muuttujan määrittely ilman alustusarvoa on määrittely. C-kieli: määrittely ilman alustusarvoa on vasta alustava määrittely. int x; // C++: määrittely. C: alustava määrittely. int x = 100; // C++: virhe, uusi määrittely! C: määrittely. C++ -kielen const-vakiot ovat käännösaikaisia vakioita #define KOKO 100 // C-kielen tapa määrittää vakio. const int koko = 100; // C++-kielen suositeltu tapa. double x[koko]; // C++: ok. C: virhe. double y[koko]; // Toimii molemmissa (mutta vältä C++:ssa). 14 13 C: '\0' jää pois. C++ sallii muuttujien määrityksen myös koodilohkon keskellä, ehtolauseessa tai for-silmukan otsakkeessa. int x = 0; x = 5; int y = x; // C++: ok. C: virhe. for(int z = 0; z < x; ++z){ // C++: ok. C: virhe. if(int q = z*x){ // C++: ok. C: virhe. std::cout << "Ei ole nolla: " << q << '\n'; C++ -kielen muutokset C-kieleen... C++ sallii staattisten muttujien alustuksen muillakin kuin vakioarvoilla. double x = sqrt(10);// C++: ok. C: virhe, ei käännösajan vakio. double y = sqrt(10); // C++: ok. C: ok (ei staattinen). static double z = sqrt(10); // C++: ok. C: virhe. Lisäksi C++ määrittelee yhden uuden primitiivitietotyypin: bool-totuusarvotyypin. bool-arvot false ja true kuvautuvat automaattisesti int-arvoiksi: false: 0 true: 1 Vastaavasti int-arvot kuvautuvat bool-arvoiksi: 0: false!= 0: true bool x = true; while(true){ // C++: ok, true on!= 0 eli vastaa C:n totta. // Ikuinen silmukka, jonka runko tässä tyhjä. 15 16

C++ -kielen hyödyllisiä perusluokkia: string C++:n standardikirjaston otsaketiedosto <string> määrittää merkkijonoluokan string. Helpottaa merkkijonojen verrattuna C-kielen char-taulukoihin. Huolehtii puolestamme esim. muistitilan varauksesta/vapautuksesta. Ylläpitää merkkijonon sisäisesti C-kielen char-taulukko-merkkijonona. Välihuomio: C++ -kielessä luokkien oliot toimivat pitkälti samalla tavalla kuin C-kielen tietue-oliot. Voidaan luoda staattisella määrityksellä tai dynaamisesti (tästä myöh.). Määrittämällä luodun olion elinkaari sama kuin tavallisen muuttujan: joko automaattinen (määrityksestä lohkon loppuun) tai staattinen. #include <string> std::string s; // Määrittely luo string-olion s.... // s vapautuu automaattisesti poistuttaessa sen lohkosta. C++ -kielen hyödyllisiä perusluokkia: string... string-merkkijono-olion alustus muuttujamäärityksen yhteydessä: Tyhjä merkkijono: ei anneta rakentimen parametreja. string mj; string mj(); tarkoittaisi funktiota, jonka palautusarvo on string. Alustus merkkijonolla: parametriksi char-taulukko tai string. string mj("c++"); tai vaihtoehtoisesti string mj = "C++";. string mj2(mj); tai vaihtoehtoisesti string mj2 = mj;. Alustus alimerkkijonolla: merkkijonon perään alkuindeksi ja pituus. string mj("c++-kieli", 2, 4); alustaa merkkijonoksi "+-ki". Alustus char-merkillä: parametriksi pituus ja merkki. string mj(10, 'Q'); alustaa merkkijonoksi "QQQQQQQQQQ". 18 17 C++ -kielen hyödyllisiä perusluokkia: string... String-merkkijonon käytöstä: Tulostus onnistuu sellaisenaan <iostream>-otsakkeen tulostusolioilla. #include <iostream> #include <string> using namespace std; int main(int argc, char *argv[]) { string s = argv[1]; cout << "Nimi oli: " << s; // Nimiavaruus std näkyviin. // Ohjelman kutsunimi. // Tulostetaan s ruudulle. Merkkijonojen yhdistäminen peräkkäin: operaattorilla + Esim. string mj3 = mj + mj2;, missä mj ja mj2 string-merkkijonoja Lisäksi sallii toisen (yllä joko mj tai mj2) olla C-kielen merkkijono. Merkkien käsittely kuin string olisi char-taulukko eli []-operaattorilla: Esim. asetus mj[2] = 'h'; on ok, jos mj vähintään 3:n pituinen string. C++ -kielen hyödyllisiä perusluokkia: string... Pari muuta alkuun hyödyllistä string-luokan jäsenfunktiota: string::size_type length(): palauttaa merkkijonon pituuden Esim. tapaan mj.length();, jos mj on string-olio. Palautusarvon tyyppi string::size_type on merkkijonoluokassa string määritetty typedef-tyyppialias. Tyypillisesti määritetty samaksi tyypiksi kuin C-kielen standardikirjaston size_t. (joka yleensä unsigned long int) string::size_type size(): palauttaa merkkijonon pituuden Käytännössä identtinen funktion length kanssa. const char * data(): palauttaa merkkijonon merkit ilman merkkiä '\0'. const char * c_str(): palauttaa C-merkkijonon eli lopussa on '\0'. Ja luku esim. char c = mj[1];, jos mj vähintään 2:n pituinen string. 19 Esim. const char *mj2 = mj.c_str();, missä mj on string-olio. 20

C++ -kielen hyödyllisiä perusluokkia: vector C++ -standardikirjaston otsake <vector> tarjoaa dynaamisen taulukon toteuttavan luokan vector. Toimii melko samantapaisesti kuin esim. Javan Vector tai ArrayList. Parametrisoitu: alkioiden tyyppi ilmaistaan kulmasulkeiden <> sisällä. vector<t> on taulukkoluokka, jonka olio tallettaa tyypin T alkioita. Esim. vector<int> v; luo int-arvoja tallettavan vector-olion v. vector<string> v; luo string-arvoja tallettavan olion v. Tyyppiä T tallettavan vector-taulukko-oliomuuttujan määritys: Koon 0 taulukko: ei rakentimen parametreja. Esim. vector<t> v;. Koon n taulukko: parametriksi n. Esim. vector<t> v(n), missä n muuttuja, tai vector<t> v(100). Tällöin kukin alkio alustetaan tyypin T alkion oletusarvolla. Esim. lukuarvot arvolla 0 ja string-oliot tyhjällä merkkijonolla. 21 C++ -kielen hyödyllisiä perusluokkia: vector... Alustusarvon voi antaa erikseen: vector<t> v(n, x); luo koon n taulukon, jonka kunkin alkion arvoksi alustetaan x. vector-taulukon alkioita voi käsitellä tavanomaisen taulukon tapaan []-indeksioperaattorilla. Muutama alkuun hyödyllinen jäsenfunktio: vector<t>::size_type size(): palauttaa taulukon alkioiden lukumäärän. Palautusarvon tyyppi vector<t>::size_type on vector-luokan sisällä määritetty typedef-alias. (lienee yleensä sama kuin size_t) void push_back(t x): lisää T-arvon/olion x taulukon perään. Taulukon koko kasvaa yhdellä. T back(): palauttaa taulukon viimeisen alkion/olion. void pop_back(): poistaa taulukon viimeisen alkion. Taulukon koko pienenee yhdellä. 22 C++ -kielen hyödyllisiä perusluokkia: vector... C++ -kielen hyödyllisiä perusluokkia: vector... Esimerkki 1: vector-taulukon alustus antamalla koko ja alustusarvo. #include <iostream> #include <vector> // Nimiavaruus std näkyviin. using namespace std; // 10 alkiota, kunkin arvoksi 100. vector<int> t(10, 100); for(int i = 0; i < t.size(); ++i) { cout << t[i] + i << '\n'; // Tulostaa arvon 100 + i. t[i] = i; // Asettaa uudeksi arvoksi t[i] = i. Esimerkki 3: vector-taulukon tyhjennys funktiolla pop_back. vector<int> t(10, 100); // 10 alkiota, kunkin arvoksi 100. while(t.size() > 0) { // Jatketaan, kunnes taulukko on tyhjä. cout << t.back() t.size() << '\n'; // Tulostetaan arvo. t.pop_back(); // Viimeinen alkio pois: taulukko pienenee. Esimerkki 2: vector-taulukon alustus tyhjäksi, alkioiden lisäys perään (samalla esimerkki kaksoisindeksoinnista tapaan t[i][j]). vector<string> t; // Määritetään tyhjä string-taulukko t. string s = "i: x"; // Merkkijono s, jota käytetään alla. for(int i = 0; i < 10; ++i) { // Lisätään taulukkoon 10 arvoa. char c = '0' + i; // Muunnetaan i numeromerkeiksi '0'...'9'. t.push_back(s); // Lisätään taulukon t perään "i: x". t[i][3] = c; // Korvataan t[i]:n merkki 'x' numeromerkillä c. cout << t.back() << " eli " << t[t.size()-1] << '\n'; 23 // Yllä kaksi eri tapaa lukea t:n viimeisen alkion arvo. Esimerkki 4: 2-ulotteinen vector<string>. vector< vector<string> > t(6); // Koon 6 päätaulukko = rivit. char m = 'A'; // Aloitetaan kirjaimesta 'A'. for(int i = 0; i < 6; ++i) { // Kukin t[i] aluksi tyhjä taulu. for(int j = 0; j < 5; ++j) { // Lisätään riville 5 arvoa. t[i].push_back(string(1, m)); // Kirjaimesta m string. if(++m > 'Z') goto VALMIS; // Kasvatetaan kirjainta m. // Kun kirjaimet 'A'...'Z' on lisätty, rikotaan silmukka. VALMIS: // Alla tulostuu 'A'...'Z' 2-ulotteisesta taulukosta. for(int i = 0; i < t.size(); ++i) // Päätaulun alkiot = rivit. for(int j = 0; j < t[i].size(); ++j) // Käy rivin t[i] läpi. cout << " " << t[i][j]; // Tulostaa merkin t[i][j]. 24

C++ -kielen hyödyllisiä perusluokkia: vector... Edellä 2-ulotteinen vector oli määritetty muodossa vector< vector<t> > Sisemmän vector-tyypin ympärillä on tarkoituksella välilyönnit. Näistä jälkimmäinen välilyönti on pakollinen: se ehkäisee loppuosan sekaannuksen operaattorin >> kanssa. Ensimmäisen välilyönnin käyttö on puhtaasti tyyliseikka/makuasia. Huom! Kurssikalvoilla ei jo tilan vuoksi edes pyritä esittelemään aivan kaikkia standardikirjaston luokkien (string, vector, jne.) jäsenfunktioita. Kurssin sivuilla ehdotetuista linkeistä cppreference.com sisältää koko standardikirjaston kuvauksen, joskin melko teknisesti esitettynä. Voi olla hieman vaikeaselkoinen aloittelevalle C++ -ohjelmoille. Vinkki 1: string-luokan kuvaus löytyy nimikkeen basic_string alaisuudesta. Vinkki 2: suuri osa jäsenfunktioita kuvaavista alasivuista sisältää lopussa myös koodiesimerkkejä, jotka ovat kohtalaisen selkeitä. C++:n funktioista C++:n funktioiden ominaisuuksia, joita C-kielessä ei ole: Funktioiden nimien kuormitus. Kahdella eri funktiolla saa olla keskenään sama nimi, jos niiden parametrilistat eroavat toisistaan. int esim(); int esim(int); // Ok: parametrilista erilainen kuin edellä. double esim(int); // Virhe: nimi ja parametrilista samat. Parametrien oletusarvot (ns. oletusparametrit). Funktion loppupään parametreille voi määrittää oletusarvoja. Määrityksen parametrilistassa parametrin perään: = alustusarvo. Jos loppupään parametreja jätetään funktiokutsussa antamatta, korvaa funktiokutsu kyseiset parametrit niiden oletusarvoilla. int kerro(int a = 0, int b = 1, int c = 1, int d = 1) { return a*b*c*d; kerro(2, 3); // Suorittaa kutsun kerro(2, 3, 1, 1), tulos = 6. Tärkeä huomio oikean muuttujatyypin käytöstä Näillä kalvoilla esiintyy tilan vuoksi mm. seuraavanlaisia koodinpätkiä: for(int i = 0; i < t.size(); ++i) // t on vector<int>-taulukko. Tämä koodi toimii oikein, jos voidaan luottaa, että int-tyyppinen muuttuja kykenee esittämään taulukon t alkioiden lukumäärän. Vertailussa i < t.size() pienempikokoisen muuttujatyypin arvolle tapahtuu tarvittaessa tyyppimuunnnos isompikokoisen tyyppiin. Kääntäjä tosin voi varoittaa etumerkillisen ja etumerkittömän arvon vertailusta (unsigned int olisi hieman parempi kuin int). Sen sijaan seuraava on yksiselitteisesti vaarallinen tilanne: unsigned int i = s.find("hakusana"); // s on string-merkkijono if(i == string::npos) // Voi olla, ettei tämä koskaan päde! Jos find palauttaa vakioarvon npos, mahtuuko se muuttujaan i? Tyypillisesti npos on suurin tyyppiin string::size_type mahtuva arvo. Yleisesti ottaen oikeaoppinen tapa on käyttää aina size_type-tyyppejä. for(vector<int>::size_type i = 0; i < t.size(); ++i){ // Nämä string::size_type i = s.find("hakusana"); // aina turvallisia.26 25 Funktion lisämääre inline. C++:n funktioista... Määrittää, että kääntäjän tulisi upottaa funktion koodi suoraan jokaiseen kohtaan, jossa funktiota kutsutaan. Tavoitteena suorituksen optimointi: erillinen funktiokutsu jää pois. Tarkoitettu korvaamaan C-kielen makrofunktiot. Inline-funktio on yhtä tehokas, mutta mahdollistaa esim. kääntäjän tavanomaiset funktioiden tyyppitarkistukset yms. Käytetään yleensä vain yksinkertaisten funktioiden kanssa (ja silloinkin lähinnä tehokriittisissä sovelluksissa). Esim. tällä kurssilla ei tarvitse käyttää. inline int kuutio(x) { return x*x*x; int x = 5; int y = 13; cout << kuutio(x); // Käännetyssä koodissa kuin cout << x*x*x; cout << kuutio(y); // Käännetyssä koodissa kuin cout << y*y*y; 28 kerro(); // Suorittaa kutsun kerro(0, 1, 1, 1), tulos = 0.27

Koodin jako erillisiin otsake- ja toteutustiedostoihin Oman koodin jako otsake- ja toteutustiedostoon samoin kuin C-kielessä:.h-päätteinen otsaketiedosto, joka sisältää rajapinnan eli ulkopuolelle tarjottavien muuttujien, tyyppien, funktioiden yms. esittelyt. Käytä moninkertaisen sisällyttämisen estoa #ifdef-direktiivillä.cpp-päätteinen kooditiedosto, joka sisältää määrittelyt/toteutukset. Sisällyttää itseensä myös oman otsaketiedostonsa. Nimiavaruuden käyttö: aseta sekä otsake- että kooditiedoston sisältämät esittelyt/määrittelyt/toteutukset kokonaan nimiavaruus-lohkojen sisään. Omaan nimiavaruuteen ei (yleensä) sisällytetä #include-direktiivejä. Luettu otsaketiedosto tulisi sisällytettyä oman nimiavaruuden alle! Jätä siis #include-määreet omien nimiavaruuslohkojen ulkopuolelle. namespace otecpp { // Alla esim. olio cout tulee määritettyä nimiavaruuteen // otecpp::std eikä std. Koodi tuskin kääntyy. #include <iostream> // Siirrä tämä ulos nimiavaruuslohkosta! Olioiden välityksestä/kopioitumisesta C ja C++ välittävät parametrit ja paluuarvot kopioina. (pass-by-value) Olioiden välitys sellaisenaan voi kätkeä pinnan alle yllättävänkin paljon resursseja kuluttavia toimenpiteitä. vector<string> kaanna(vector<string> t) { // Kääntää alkiot. vector<string>::size_type v = t.size(); for(vector<string>::size_type u = 0; u < v; ++u, --v) { string tmp = t[u]; t[u] = t[v-1]; // Indeksi v-1, koska aluksi v = t.size(): t[v-1] = tmp; // alustus v = t.size()-1 olisi vaarallinen, // koska t.size() voi olla 0 ja size_type on etumerkitön. return t; // Palauta t, jonka alkiot käännetty takaperin. 29 30 Olioiden välityksestä/kopioitumisesta... C-kielen tapa välttää suurten parametrien kopioinnista aiheutuvaa työtä: parametrin välitys osoittimen kautta. vector<string>* kaanna2(vector<string> *p) { // Kääntää alkiot. vector<string>::size_type v = p->size(); for(vector<string>::size_type u = 0; u < v; ++u, --v) { string tmp = (*p)[u]; // Sulkeet, koska [] vahvempi kuin *. (*p)[u] = (*p)[v-1]; (*p)[v-1] = tmp; return p; // Nyt p:n osoittaman taulun alkiot takaperin. Kutsu vector<string> k = *kaanna2(&t); luo uuden vector<string>-taulukon vain kerran: kun taulukko k luodaan. Kaksi muuta (mahdollista) kopiointia muuttuivat resurssien kannalta tehokkaiksi osoittimien arvojen kopioinneiksi. Heikkouksia(?): aiheuttaa muutoksia syntaksiin, ja osoittimet muutenkin hieman riskialttiita (osoitettavaa kohtaa voi muuttaa). Kutsu vector<string> k = kaanna(t); luo uuden vector<string>taulukon pahimmillaan 3 kertaa! (riippuu kääntäjästä...) 1. Parametrista t luodaan kopio, joka välitetään funktiolle kaanna. 2. Palautusarvosta t luodaan kopio, joka välitetään kutsujalle. 3. Taulukko k luodaan palautusarvona saadun vector-taulun pohjalta. C++ -kielen viitteet C++ tarjoaa uuden tavan välittää parametreja ilman kopiointia: viitteet. Viite on ikäänkuin alias, joka viittaa kiinteästi johonkin muuttujaan. Viite on oma tyyppikategoriansa (samoin kuin osoittimet ovat omansa). Mikä tahansa tyyppi, paitsi jo valmiiksi viitetyyppi, voidaan muuntaa viitetyypiksi lisäämällä sen perään viitemääre &. int & on int-viite, vector<string> & on vector<string>-viite, double *& on viite double-osoittimeen, ja niin edelleen. Viitemääre & ja osoitinoperaattori & tarkoittavat/tekevät eri asioita! Viitemuuttuja on aina sidottu johonkin olemassaolevaan muuttujaan: sidos kohdistuu viitemuuttujan alustusarvoon, eikä sitä voi enää vaihtaa. C++:ssa ei ole null-viitettä : voi viitata vain todelliseen muuttujaan! int x = 100; int &y = x;// Määritetään int-viite y muuttujan x aliakseksi. y += 5; // y viittaa muuttujaan x eli x = 100 + 5 = 105. 31 32

C++ -kielen viite vs. Javan viite Javan viite toimii lähes kuin C/C++:n osoitin.(ilman osoitinaritmetiikkaa) Javan viite on erillinen muuttuja, jonka arvo on viite johonkin olioon. Viite voidaan muuttaa viittaamaan (osoittamaan) muualle. C++:n viite on kiinteä viittaus suoraan johonkin muuttujaan itseensä. Viitteellä ei ole omaa arvoa tai osoitetta. Se on vain alias. Jos x on viite muuttujaan y, pätee &x = &y eli osoitteet ovat samat. Kaikki kohdat, joissa viite esiintyy, toimivat aivan kuin viitteen tilalle korvattaisiin suoraan se muuttuja, johon viite viittaa. // Java: String a = new String("C"); String b = new String("C++"); a = b; // Viite a viittaa nyt samaan String-olioon kuin b. // C++: string a("c"); string b("c++"); string *aos = &a; // Huom! Nämä &:t ovat osoitinoperaattoreita. string *bos = &b; // Viitemääre & voi esiintyä vain tyypissä. a = b; // a:n sisällöksi kopioituu b:n sisältö. aos = bos; // aos osoittaa nyt samaan string-olioon kuin bos.33 C++ -kielen viitteet... Pakollinen viite-esimerkki: kahden arvon vaihtaminen keskenään. void vaihda(double &a, double &b) {// Vaihtaa a:n ja b:n arvot. double tmp = a; a = b; // Alkuperäinen parametri a saa b:n arvon. b = tmp; // Alkuperäinen parametri b saa a:n vanhan arvon. double x = 3.14; // Pii. double y = 2.72; // Neperin luku. vaihda(x, y); // Funktiokutsu saa alkuperäiset x:n ja y:n. cout << x << " " << y; // Tulostuu "2.72 3.14". Olion kopioinnin välttäminen: parametri ja/tai palautusarvo viitteenä. vector<string>& kaanna(vector<string> &t) { // Kääntää alkiot. vector<string>::size_type v = t.size(); for(vector<string>::size_type u = 0; u < v; ++u, --v) { string tmp = t[u]; t[u] = t[v-1]; t[v-1] = tmp; // Myös palautusarvo viitteenä eikä kopiona: tässä return t; // palautetaan sama viiteparametri takaisin. C++ -kielen viitteet... Viitteiden tärkein käyttökohde: parametrien ja palautusarvojen välitys. Jos funktioparametri määritetään viiteparametriksi, ei parametria välitetä kopiona, vaan funktio saa suoraan alkuperäisen parametrin. void kopio(int i) { // Parametri i saadaan kopiona. i = 200; // Muuttaa kopion arvoksi 200. // Kopio i tuhoutuu eikä siihen tehty asetus näy ulos. void viite(int &i) { // Parametri on määritetty viitteeksi. i = 100; // i on alkuperäisen parametrin alias. Tämä rivi // toimii aivan kuin i:n tilalla olisi alkuperäinen muuttuja! int x = 0, y = 0; kopio(x); // Funktiokutsu saa muuttujan x kopion. viite(y); // Funktiokutsu saa viitteen muuttujaan y. cout << x << ":" << y; // Tulostuu 0:100, koska viite muutti // arvoa y mutta kopio ei arvoa x. Viiteparametrin käyttöön on lähinnä kaksi motiivia: 1) Suurten parametrien kopionnin aiheuttaman lisätyön välttäminen. Edellyttää, että kopiointi todella on tarpeetonta. 2) Jos funktion halutaan muokkaavan suoraan alkuperäistä parametria. 34 Palautusarvo viitteenä Älä tee näin: paikallisen olion palauttaminen viitteenä. vector<int>& arvo(int koko) { // Palauttaa vector-viitteen. vector<int> t(koko); // Automaattinen vector-muuttuja. for(int i = 0; i < koko; ++i) { t[i] = rand(); // Arvotaan <cstdlib>:n funktiolla rand. return t; // Palauttaa viitteen paikalliseen muuttujaan t. // Ongelma: t vapautuu automaattisesti, kun funktio palaa! vector<int> luvut = arvo(10); // Vaara: luvut alustetaan // funktion arvo paikallisella oliolla t, joka ei // funktiokutsun palattua ole enää olemassa! Nyrkkisääntö: jos haluat palauttaa uuden olion, palauta se kopiona. Toki toinen vaihtoehto on käyttää dynaamista muistinhallintaa ja osoittimia, mutta lisähankaluutena mm. vastuu muistinvapautuksesta. Funktiokutsun jälkeen olemassaolevan muuttujan saa palauttaa viitteenä. Esim. viittenä saadun parametrin voi palauttaa takaisin viitteenä. // Huom! Viitettä paikalliseen muuttujaan ei saisi palauttaa!35 36

Parametri viitteenä Viiteparametri mahdollistaa suoraan alkuperäisen arvon muuttamisen, joka ei välttämättä ole toivottavaa. Muuttamisen voi estää määrittämällä parametrin vakioviitteeksi. Viiteparametrille määre const. void esim(const vector<int> &t) { // Parametri on vakioviite. t[0] = 999; // Käännösvirhe: yritys muuttaa vakioparametria! Nyrkkisääntö: ota viiteparametri aina vakioviitteenä, ellei funktion tarkoitus nimenomaan ole muuttaa parametria. Tämä ei ole pelkästään suositus vaan toisinaan pakko: C++ ei salli vakiomuuttujan välittämistä funktiolle tavallisena viiteparametrina. void f(string &mj) { // Parametri on tavallinen viite. cout << mj; // Tulostetaan mj. const string s("c++"); f(s); // Käännösvirhe: const ei kelpaa viiteparametriksi! Parametri viitteenä... Huomaa, että const on parametrin yhteydessä laajentava määre. const-parametriksi kelpaa sekä tavallinen että vakioarvo. Yhteenveto funktion parametrin tyypin valinnasta: Ota tavalliseen tapaan kopiona, jos parametri on yksinkertainen eikä sitä haluta muuttaa. Esim. primitiivityypit ja osoittimet ovat yksinkertaisia. Tässä tapauksessa vakiomääreellä ei erityisemmin ole merkitystä. Ota osoittimena, jos parametri on monimutkainen (kallis kopioida) ja haluat mahdollistaa tyhjän arvon (NULL-osoittimen) välittämisen. Ota osoittimena vakioon, ellei osoitettua parametria haluta muuttaa. Ota viiteparametrina, jos parametri on monimutkainen eikä koskaan tyhjä. // Kutsu olisi ok, jos f olisi muotoa f(const string &mj). 37 Muistaen määre const, ellei parametria nimenomaan Ohjelmoinnin haluta muuttaa.38 tekniikka C++ (Heikki Hyyrö / Tampereen yliopisto) C++ -tyyliset tyyppimuunnokset C-kielen tyyppimuunnosta voidaan pitää arveluttavana. const char a[] = "C++"; // Vakio-C-merkkijono a = "C++". char *b = (char *) a; // b osoittaa merkkijonoon a. b[1] = b[2] = '-'; // Nyt b:n kautta muutettiin a = "C--". Mahdollistaa toiminnallisesti merkittävät muunnokset ilman, että se erityisemmin pistäisi silmään koodissa. Esim. yllä kierrettiin a:n vakiomääre, joka lienee arveluttavaa/virhe. Tyyppimuunnos (char *) ei kuitenkaan hyppää silmille. C++:n tarjoaa omat turvallisemmat tyyppimuunnosoperaatiot: static_cast<mihin>(mitä): tyyppimuuntaa arvon mitä tyyppiin mihin. Sallii muunnokset keskenään samankaltaisten tyyppien välillä, kuten lukutyypistä toiseen tai samankaltaisesta osoitintyypistä toiseen. char *b = static_cast<char *>(malloc(100)); // char-taulukko. Ei salli const-määreen poistoa. const char a[] = "C++"; // Vakio-C-merkkijono a = "C++". char *b = static_cast<char *>(a); // Käännösvirhe: a on const! 39 C++ -tyyliset tyyppimuunnokset... const_cast<mihin>(mitä) Sallii muunnokset keskenään yhteensopivien tyyppien välillä, jotka eroavat const-määreiden suhteen. const char a[] = "C++"; // Vakio-C-merkkijono a = "C++". char *b = const_cast<char *>(a); // Ok: const-tyyppimuunnos. reinterpret_cast<mihin>(mitä) reinterpret_cast on järjestelmäriippuvainen muunnos. Sallii muunnoksia sellaisten tyyppien välillä, joiden välillä ei ole välttämättä luonnollista vastaavuutta. Lähinnä seuraavat: Osoitin kokonaisluvuksi ja päinvastoin. Funktio-osoittimen muunto osoittamaan toisenlaiseen funktioon. T-osoittimen muunto U-osoittimeksi (missä T ja U jotkin tyypit). Ei kuitenkaan salli const-määreen poistoa. dynamic_cast<mihin>(mitä): luokan tyyppimuunnos, jonka laillisuus tarkistetaan ajonaikana. (tästä lisää myöhemmin) 40

Syötteen luvusta ja tulostuksesta (I/O-operaatiot) C++ -standardikirjaston pääasialliset tulostus- ja syötevirtoja käsittelevät luokat (ja niiden perintähierarkia): istream ostream istringstream ifstream iostream stringstream istream: syötteen luku standardisyötevirrasta. ostream: tulostus standarditulostevirtaan. Esim. cout on ostream-olio. fstream ostringstream iostream: luokkien istream ja ostream yhdistelmä (moniperintä). Kokoaa yhteen sekä luku- että tulostusominaisuudet. ifstream, ofstream ja fstream: periytyvät luokista istream, ostream ja iostream ja ovat erikoistuneet tiedostovirtoihin. istringstream, ostringstream ja stringstream: kuin edellä, mutta ofstream ovat erikoistuneet string-pohjaisiin merkkijonovirtoihin. 41 Syötteen luku... Luku >> -operaattorilla toimii täsmäämättömän syötteen suhteen samoin kuin C-kielen scanf-funktiot: täsmäämättömät merkit jäävät syötevirtaan. #include <iostream> using namespace std; int a = 1, summa = 0; while(a!= 0) { // Luetaan käyttäjältä arvoja, jotka!= 0. cin >> a; // Yritetään lukea kokonaisluku. summa += a; // Lasketaan lukujen summa. Esimerkiksi jos käyttäjä syöttäisi edellä sanan "ääh", ei kokonaisluvun täsmäystä yrittävä cin saisi kulutettua yhtään syötemerkkiä. "ääh" jäisi kerta toisensa jälkeen syötevirtaan (ikuinen silmukka). Eräs tapa välttää ongelma: luetaan syöte ensin string-olioon. string sana; // Luetaan syötteen seuraava sana tähän. cin >> sana; // string-oliolle kelpaa mikä tahansa sana. a = atoi(sana.c_str()); // Muunto. Voisi tarkistaa virheen. Syötteen luku istream: syötteen luku syötevirrasta. Syötteen luku tapahtuu operaattorilla >>. Toimii analogisesti tulostuksesta tutun operaattorin << kanssa. Ohjaa luetun arvon istream-lukuoliosta johonkin muuttujaan. Tukee valmiiksi tavanomaisia muuttujatyyppejä: primitiivityypit, char-taulukot, string-merkkijonot. Otsakkeet iostream/istream määrittävät standardisyötevirrasta (eli C-kielen syötevirrasta stdin) lukuun valmiin istream-olion cin. #include <iostream> // Tässä riittäisi myös <istream>. using namespace std; string s; double y; cin >> s >> y; // Lukee käyttäjältä ensin sanan muuttujaan s // ja sitten liukuluvun muuttujaan y. Oletusarvoisesti >> hyppää syötteen alussa mahdollisesti esiintyvän tyhjän tilan (välilyönnit, sarkaimet, rivinvaihdot jne.) yli. 42 Syötteen luku... Niin luku- kuin syötevirtaolioiden tilaa voi tutkia mm. jäsenfunktioilla: bool good(): palauttaa true, jos olion viimeisin operaatio onnistui. Jos good() palauttaa false, ei olion kautta voi lukea tai kirjoittaa ennen virhetilan nollaamista. bool eof(): palauttaa true, jos syötettä yritetään lukea sen jo loputtua. bool fail(): palauttaa true, jos virran yhteydessä on tapahtunut virhe. Esim. luku epäonnistunut syötteen loppumisen tai muun syyn vuoksi. Jos fail() palauttaa true, ei olion kautta voi lukea tai kirjoittaa ennen virhetilan nollaamista. Olion sisäisen virhetilan voi nollata jäsenfunktiokutsulla void clear(). Luku/syöteolio ainoastaan muuttaa omaa sisäistä kirjanpitoaan: pelkkä tämän nollauksen teko ei korjaa virhetilanteeseen johtanutta syytä! Virtaolio itsessään evaluoituu arvoksi!fail(). if(cin) // Päteekö!cin.fail(). cin >> sana; // Jos pätee, voidaan yrittää lukea. 44 summa += a; // Lasketaan vaikkapa lukujen summa. 43

Syötteen luku... Lukuoperaatio virta >> a, missä a:n tyyppi on T, vastaa oikeastaan funktion istream & operator >>(istream &virta, T &a) kutsua. Operaation palautusarvo on viite lukuvirtaan virta. Käyttökohde 1: palautusarvo mahdollistaa ketjutuksen, sillä virta >> a >> b; tarkoittaa oikeastaan operaatiota (virta >> a) >> b; virta >> a palauttaa arvon virta, josta seuraa kutsu virta >> b. Käyttökohde 2: lukutoimenpiteen onnistumisen tutkiminen. if(virta >> a) suorittaa lukuoperaation virta >> a ja palauttaa olion virta, joka puolestaan evaluoituu ehtolauseeksi!virta.fail(). while(a!= 0) { // Luetaan käyttäjältä arvoja, jotka!= 0. if(cin >> a) // Yritetään lukea kokonaisluku. summa += a; // Lasketaan lukujen summa. else { // Tähän haaraan tullaan, jos luku epäonnistui. cout << "Virhe tai laiton syöte, joten lopetetaan\n"; break; // Poistutaan while-silmukasta. Syötteen luku... istringstream-olion käyttö on yksi vaihtoehto esim. kokonais- tai liukulukumuunnoksiin merkkijonomuodosta. (vrt. C-kielen sscanf) #include <iostream> #include <sstream> using namespace std; int ika = 0; double paino = 0; float pituus = 0; cout << "Anna ikä, paino ja pituus: "; string sana; Syötteen luku... istringstream: syötteen luku string-oliota lukevasta syötevirrasta. Huomautus: stringstream-luokat löytyvät otsaketiedostosta sstream. istringstream-olio toimii muuten samoin kuin istream-olio, mutta istringstream-olion syötevirtana toimii jokin string-olio. Syötteenä toimivan string-olion voi antaa rakentimen parametrina. Sen voi myös asettaa jäsenfunktiolla void str(const string &mj) Nykyisen syöte-string-merkkijonon saa jäsenfunktiolla string str() #include <sstream> using namespace std; string a; // string-olio osaa huolehtia itse omasta koostaan. char b[4]; // Tähänkin luetaan alla. Koon oltava riittävä! int c; // Ja lisäksi int-muuttuja c char d, e; // sekä kaksi char-muuttujaa d ja e. string s = "Ote C++ 5 op"; // Merkkijono, jota alla luetaan. istringstream syote(s); // syote-olio lukee merkkijonoa s. syote >> a >> b >> c >> d >> e; // Tämä rivi lukee ja asettaa // arvot a = "Ote", b = "C++", c = 5, d = 'o' ja e = 'p'. 45 // Alla tehdään merkkijonon // asetuksen jälkeen clear(), // jotta olion mahdollinen // virhetila on nollattu ennen // uuden merkkijonon lukua. cin >> sana; // Luetaan ikä sanaan merkkijonona. istringstream s(sana); // Luetaan sanan sisältöä oliolla s. s >> ika; // Luetaan ikä int-arvona. cin >> sana; // Luetaan paino sanaan merkkijonona. s.str(sana); s.clear(); // Resetoidaan luettava merkkijono. s >> paino; // Luetaan paino double-arvona. cin >> sana; // Luetaan pituus sanaan merkkijonona. s.str(sana); s.clear(); // Resetoidaan luettava merkkijono. s >> pituus; // Luetaan pituus float-arvona. cout << "Annoit tiedot: ikä=" << ika << ", paino=" << paino << " ja pituus=" << pituus << '\n'; Syötteen luku... ifstream: syötteen luku tiedostosta. (vrt. C-kielen fscanf) Huomautus: fstream-luokat löytyvät otsaketiedostosta fstream. ifstream-olio toimii muuten kuin istream-olio, mutta lukee tiedostosta. Avattavan tiedoston nimen voi antaa suoraan rakentimen parametrina. Tai voi avata jäsenfunktiolla void open(const char *nimi). C++03: nimi annettava C-merkkijonona (string ei käy). ifstream-olion tiedoston voi sulkea jäsenfunktiolla void close(). Ellei ole jo suljettu, tiedosto suljetaan, kun ifstream-olio tuhoutuu. #include <fstream> using namespace std; int main(int argc, char *argv[]) { ifstream tied(argv[1]); // Avataan tiedosto argv[1]. string nimi2; cin >> nimi2; // Kysytään toinen tiedoston nimi käyttäjältä. tied.close(); // Suljetaan tiedosto argv[1]. tied.open(nimi2.c_str()); // Avataan tiedosto nimi2. // Olio tied tuhoutuu ja tiedosto nimi2 suljetaan. 47 46 48

Syötteen luku... Tiedoston avauksessa voi nimen jälkeen määrittää avausmoodin: Moodeille on määritetty valmiit vakioarvot mm. luokassa ios_base, joka automaattisesti sisältyy mukaan otsaketiedoston fstream mukana. app: tiedostoon tehtävät kirjoitukset kohdistuvat sen loppuun. binary: avataan binäärimoodissa. in: avataan lukua varten (tämä on ifstream:n oletusarvo). out: avataan kirjoitusta varten (tämä on ofstream:n oletusarvo). trunc: tyhjätään tiedoston vanha sisältö. ate: siirrytään avauksen jälkeen tiedoston loppuun. Moodit on määritetty toisistaan erillisiksi bittiarvoiksi: avausmoodiin voi yhdistää useita moodeja bitti-or-operaattorilla. #include <fstream> using namespace std; int main(int argc, char *argv[]) { // Alla ios_base::in on tarpeeton, koska kyseessä ifstream ifstream tied(argv[1], ios_base::in ios_base::binary); ifstream tied2(argv[1], ios_base::binary); // Sama kuin yllä49 Syötteen luku... Esim. yksittäisten sanojen poiminta merkkijonosta: string s; // Oliolle v parametriksi istringstream v(string("yksi 2 3.0")); // nimetön string-olio. while(v >> s) // Luetaan niin monta sanaa, kuin voidaan. cout << "Oli sana: " << s << '\n'; // Tulostetaan sana. Esim. tiedoston kaikkien rivien luku: string s; ifstream v("syote.txt"); // Luetaan tiedostoa "syote.txt". while(getline(v, s)) // Luetaan niin monta riviä, kuin voidaan. cout << "Oli rivi: " << s << '\n'; // Tulostetaan rivi. Esim. tiedoston kaikkien merkkien (myös tyhjä tila) luku: char c; ifstream v("syote.txt"); // Luetaan tiedostoa "syote.txt". while(v.get(c)) // Luetaan niin monta merkkiä, kuin voidaan. cout << "Oli merkki: " << c << '\n'; // Tulostetaan merkki. Varsinkin rivien/sanojen luku selvästi helpompaa kuin C-kielessä? Syötteen luku... Operaattorin >> lisäksi istream (ja siten myös istringstream ja ifstream) tukevat mm. seuraavia lukufunktioita: Jäsenfunktio istream & get(char &m): lukee seuraavan merkin syötteestä ja asettaa sen viiteparametrina saatuun char-muuttujaan. Ei hyppää tyhjän tilan, kuten välilyöntien tai rivinvaihtojen, yli. Funktio istream & getline(istream &v, string &s, char e = '\n'): lukee string-olioon s syötevirran v merkkejä merkin e esiintymään asti. Ellei parametria e anna, luetaan syötettä merkkiin '\n' asti eli yksi rivi. Merkki e luetaan syötteestä, mutta sitä ei lisätä merkkijonon s perään. Huom! Tämä funktio on määritetty otsaketiedostossa <string>. Kumpikin edellisistä palauttaa parametrina saamansa istream-olion. Palautusarvoa voidaan siten esim. käyttää ehdon!v.fail() tutkimiseen. Tulostus 50 ostringstream: tulostus tallentuu merkkijonoon. (vrt. C-kielen sprintf) Merkkijonoa ylläpidetään ostringstream-olion sisäisessä muistissa. Olioon tallentuneen merkkijonon saa jäsenfunktiolla string str(). Jäsenfunktiolla void str(const string &mj) voi korvata nykyisen tulostuksen parametrin mj esittämällä merkkijonolla. Mutta tällöin tulostuskohta resetoituu merkkijonon alkuun! Tulostuskohdan voi siirtää loppuun jäsenfunktiolla seekp. ostringstream v; for(double d = 1.5; d < 5; d += 1) v << " " << d; // Tulostetaan d:n käymät arvot merkkijonoon. string s = v.str(); // Luetaan " 1.5 2.5 3.5 4.5" oliosta v. v.str("c"); // Nyt olion v sisällä on "C". v << "++"; // Yritetään tulostaa perään "++". cout << v.str(); // Ruudulle tulostuukin "++". v.str("c"); // Nyt olion v sisällä on taas "C". v.seekp(0, v.end); // Siirretään tulostuskohta loppuun. v << "++"; // Yritetään taas tulostaa perään "++". cout << v.str(); // Ruudulle tulostuu "C++". 52 51

Tulostus... ofstream: tulostus ohjautuu tiedostoon. (vrt. C-kielen fprintf) Tiedoston avaus samalla tavalla kuin aiemmin ifstream-olioilla: Tiedoston nimi rakentimelle, tai avaus erikseen jäsenfunktiolla open. Nimen jälkeen voi antaa avausmoodin. Myös tiedoston sulkeminen samalla tavalla: Sulkeutuu automaattisesti, kun ofstream-olio tuhoutuu. Voi myös sulkea erikseen jäsenfunktiolla close. ofstream tied("koe.txt"); for(char c = 'A'; c <= 'Z'; ++c){ // Tulostaa isot aakkoset. tied << " " << c; tied.close(); tied.open("koe.txt", ios_base::app); // Jatketaan tiedostoa. for(char c = 'a'; c <= 'z'; ++c){ // Tulostaa pienet aakkoset. tied << " " << c; tied.close(); // Tiedostossa "koe.txt" isot ja pienet aakkoset: // A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c... Virran muotoiluasetukset... fmtflags setf(fmtflags f): lisää olion lippubitteihin arvon f bitit. Vastaa kutsua flags(flags() f). fmtflags unsetf(fmtflags f): poistaa olion lippubiteistä arvon f bitit. Vastaa kutsua flags(flags() & ~f). fmtflags setf(fmtflags f, fmtflags m): korvaa arvon m ilmaisemat bitit arvon f biteillä. Vastaa kutsua flags((flags & ~m) (f & m)). Luokka ios_base määrittää eri ominaisuuksia koskevia lippuvakioarvoja. Tässä esimerkkinä kaksi: skipws: hypätäänkö tyhjän tilan (kuten välilyöntien) yli. boolalpha: ilmaistaanko bool-arvot 1/0 merkkijonoina "true"/"false". bool b; cin.setf(ios_base::boolalpha); // "true"/"false"-tila päälle. cin.unsetf(ios_base::skipws); // Ei hypätä tyhjän tilan yli. cin >> b; // Jos käyttäjä antaa "true", on b 1 eli true. Mutta Virran muotoiluasetukset C++ -standardikirjaston I/O-virrat sallivat monia niiden käyttäytymistä koskevia muotoiluasetuksia. Koskevat esim. tulostuksen muotoilua tai syötteen luvun toimintaa (vastaavat jossain määrin esim. printf/scanf-funktioiden lisämääreitä). Virta-olio ylläpitää muotoiluasetuksensa sisällään: 1) Ns. lippumuuttujissa. Yksittäiset liput ovat toisistaan erillisiä bittivakioita, joita voi yhdistellä bitti-or-operaatiolla. Luokassa ios_base on typedef-määritetty lippubittien käsittelyyn sopiva kokonaislukutyyppialias fmtflags. 2) Tavallisina arvoina (ominaisuudella on jokin arvo). Olion lippumuuttujia voi käsitellä mm. jäsenfunktioilla flags ja setf: fmtflags flags(): palauttaa olion lippubitit kuvaavan fmtflags-arvon. fmtflags flags(fmtflags f): korvaa lippubitit parametrin f lippubiteillä, palauttaa korvatut (vanhat) lippubitit. 54 53 Virran muotoiluasetukset... Lisäksi esim. tulostuksen leveyttä ja tarkkuutta voi säätää jäsenfunktioilla width ja precision: streamsize width(): palauttaa nykyisen tulostusleveyden. streamsize on typedef-määritetty kokonaislukutyyppialias. streamsize width(streamsize n): asettaa tulostusleveydelle arvon n. Asetus säilyy voimassa vain seuraavan tulostuksen ajan. Palauttaa asetusta edeltäneen (vanhan) tulostusleveyden. streamsize precision(): palauttaa nykyisen tulostustarkkuuden. streamsize precision(streamsize n): asettaa tarkkuudelle arvon n. Palauttaa asetusta edeltäneen (vanhan) tulostustarkkuuden. double pii = 3.14; cout.setf(ios_base::showpoint); // Loppunollatkin tulostuvat. cout.width(10); // Tulostus 10 merkin leveydellä. cout.precision(5); // Tulostus 5 numeron tarkkuudella. cout << pii; // Tulostuu muodossa " 3.1400". cout.unsetf(ios_base::showpoint); // Loppunollia ei tulosteta. cout << pii; // Nyt tulostuu muodossa "3.14". 56 // esim. syötteillä " true" tai "1" saisi b arvon 0 eli false. 55

Manipulaattorit Luku- ja tulostusolioille on mahdollista välittää esim. asetuksia tekeviä funktiokutsuja (ns. manipulaattoreita) operaattoreiden >> ja << kautta. Manipulaattori: erikoisfunktio, jonka operaattori >> tai << suorittaa saadessaan sellaisen parametriksi. Mahdollistaa asetusten upotuksen suoraan luku- tai tulostuslauseisiin. Otsakkeet <iostream> ja <iomanip> määrittävät mm. seuraavanlaisia manipulaattoreita: Lähes jokaiselle lipulle X on manipulaattori X (ja ehkä myös nox): X asettaa lipun X päälle ja nox pois (jos on nox): esim. skipws ja noskipws. endl: tyhjennä tulostuspuskuri ja tulosta rivinvaihto. setw(int n): aseta tulostusleveydeksi n. (<iomanip>) setprecision(int n): aseta tulostustarkkuudeksi n. (<iomanip>) double pii = 3.14; // Aiempi esimerkki, nyt manipulaattoreilla. cout << showpoint << setprecision(5) << setw(10) << pii; Dynaaminen muistinhallinta a'la C++ C++ -kielessä on omat operaattorit dynaamiseen muistinvaraukseen ja vapautukseen: new ja delete. Perusperiaate sama kuin C-kielessä: dynaamisesti varattua muistia käsitellään osoittimien kautta. (ja vapautuksesta pitää huolehtia itse!) new palauttaa osoittimen varattuun muistitilaan. Jos varataan tilaa luokan oliolle, new alustaa olion sen rakentimella. new T(alustus): varaa sizeof(t) tavua tilaa, palautusarvo on T-osoitin. (alustus) ilmaisee luodun arvon alustusarvon, jos T on primitiivityyppi, ja rakentimen parametrit, jos T on luokka. Voidaan jättää pois, ellei T luokka, jolla ei parametritonta rakenninta. int *x = new int; luo dynaamisesti yhden alustamattoman int-arvon. int *y = new int(100); luo dynaamisesti int-arvon 100. string *s = new string; luo ja oletusalustaa string-olion (tyhjäksi). cout << noshowpoint << pii; // Yllä " 3.1400", tässä "3.14". 57 string *t = new string(3, 'C'); luo string-merkkijonon "CCC". 58 Dynaaminen muistinhallinta a'la C++... new T[n]: varaa sizeof(t)*n tavua tilaa, palautusarvo on T-osoitin. Eli varaa n-alkioisen T-taulukon Arvon n oltava ei-negatiivinen kokonaisluku. Arvojen alustus: Jos T on primitiivityyppi, ei taulukon arvoja alusteta. Jos T on luokka, alkiot alustetaan parametrittomalla rakentimella. Luokalla T on tällöin oltava tällainen ns. oletusrakennin! int *x = new int[100]; varaa alustamattoman 100-alkioisen int-taulukon. double ***t = new double **[a]; varaa alustamattoman a-alkioisen double **-taulukon. Voisi olla a b c-kokoisen kolmiulotteisen taulukon päätaulukko. string *t = new string[x]; varaa x-alkioisen string-taulukon, jonka Dynaaminen muistinhallinta a'la C++... new-operaattorilla varattu muisti pitää vapauttaa delete-operaattorilla. delete osoitin: vapauttaa perus-new-operaattorilla varatun muistin. vector<int> *iv = new vector<int>; // Varataan vector-olio. double *y = new double(3.14); // Varataan double-arvo 3.14. delete iv; // Vapautetaan ja tuhotaan vector-olio iv. free(y); // Virhe: pitää käyttää delete-operaattoria. delete y; // Ok. delete [] osoitin: vapauttaa taulukko-new-operaattorilla varatun tilan. Jos varauksessa käytettiin taulukkomuotoa new T[n], pitää kyseinen muisti vapauttaa tällä delete-operaattorin taulukkoversiolla. char **mjt = new char *[100]; // Varataan C-merkkijonotaulukko. string *st = new string[50]; // Varataan string-taulukko. delete [] mjt; // Taulukon vapautus muodossa delete [] osoitin. delete st; // Virhe: st on taulukko eli pitäisi olla delete []. delete [] st; // Ok. kukin alkio on alustettu tyhjäksi merkkijonoksi. 59 60

C++ -kielen luokat struct EsimerkkiLuokka { // C++:n struct onkin luokka! int x; // Tämä jäsen on julkinen (structin oletus public). private: // Saantimääre private: aloittaa yksityisen lohkon. int y; // Tämä jäsen on yksityinen. // Saantimääre aloittaa julkisen lohkon. ; // Ei saa unohtaa puolipistettä lopusta! C++ -kielessä C-kielen struct-tietueet on laajennettu luokiksi. C++:n tietueiden (eli luokkien) yhteydessä voi esim. rajoittaa jäsenten käyttöä, luoda luokka- tai jäsenfunktioita, käyttää perintää, jne. Luokan voi määrittää joko avainsanaa struct tai class käyttäen. Ainoa ero on jäsenten oletus-saantimääre: struct: kaikki jäsenet ovat public, ellei erikseen muuta määritetä. class: kaikki jäsenet ovat private, ellei erikseen muuta määritetä. Luokan jäsenten saantimääre määritetään muotoa saantimääre: olevien otsakkeiden avulla. Otsakkeen jälkeiset jäsenet saavat otsakkeen mukaisen saantimääreen. C++ -kielen luokat... Luokan olion muodostamisen yhteydessä suoritetaan luokan rakennin. Jäsenfunktio, jonka nimi on sama kuin luokan nimi ja jolle ei määritetä palautusarvon tyyppiä (ei edes void). Esimerkki: yksinkertainen murtolukua kuvaava luokka Murtoluku: class Murtoluku { // class, joten oletussaantimääre on private int os; // Murtoluvun osoittaja, yksityinen int nim; // Murroluvun nimittäjä, yksityinen // Tästä alkaa julkisten jäsenten määritykset Murtoluku(int o, int n) { // Julkinen rakennin os = o; // Alustetaan luotavan Murtoluku-olion arvot nim = n; // rakentimen saamien parametrien mukaisiksi int getos() { return os; // Osoittajan lukufunktio. void setos(int o) { os = o; // Osoittajan asetusfunktio. int getnim() { return nim; // Nimittäjän lukufunktio. void setnim(int n) { nim = n; // Nimittäjän asetusfunktio. double likiarvo() { return (double) os/nim; // Muu funktio. ; // Ei saa unohtaa puolipistettä lopusta! Esim. Murtoluku m(4, 5); määrittäisi Murtoluku-olion 4/5, ja tällöin cout << m.likiarvo(); tulostaisi ruudulle arvon 0.8. 62 Vaikutus ulottuu seuraavaan otsakkeeseen (tai loppuun) Ohjelmoinnin tekniikka asti. C++ (Heikki Hyyrö / Tampereen yliopisto) 61 Luokan oletusrakennin Luokan olion luonnin yhteydessä suoritetaan aina sen jokin rakennin. C++03: luokan X oliota luotaessa suoritetaan tasan yksi luokan X rakennin. (rakennin ei voi kutsua toista rakenninta; esim. Javassa voi) Ellei luokkamäärittely sisällä yhtään rakenninta, luo kääntäjä luokalle ns. oletusrakentimen, joka ei ota parametreja ja jolla on tyhjä runko. X() { // Luokan X Oletusrakentimen muoto: ei ota parametreja // eikä alusta mitään. Jos luokalle määrittää yhdenkin rakentimen, ei oletusrakenninta luoda. Tällöin luokalla ei välttämättä ole parametritonta rakenninta. Jos ei ole, oliota ei voi luoda ilman parametreja esim. tapaan X x;. struct A { int a; A(int i) { a = i; // Rakennin, ei oletusarvoa. ; // Määritys A x; laiton (A:n rakennin vaatii parametrin). struct B { int b; B(int i = 0) { b = i; // Rakennin, oletusarvo i = 0. This-osoitin Luokan olion jäsenfunktioilla on käytettävissään ns. this-osoitin. Avainsana this toimii jäsenfunktion sisällä osoittimena olioon itseensä. Se olio, jonka kautta jäsenfunktiota kutsuttiin. Vrt. Javan this-viite. Käytetään, jos halutaan viitata eksplisiittisesti olioon itseensä tai johonkin sen jäseneen. Esim. jos Murtoluku-luokan rakennin ja asetusfunktiot olisivat ottaneet parametrinsa samannimisinä kuin jäsenet os ja nim: Murtoluku(int os, int nim) { // Jäsenet os ja nim peittyvät. this->os = os; // Viittaus jäseniin this-osoittimen kautta. this->nim = nim; void setos(int os) { // Parametri os peittää jäsenen os. this->os = os; // Viittaus jäseneen this-osoittimen kautta. 64 ;// Määritys B x; on ok (B:n rakennin ei vaadi parametria).63