Johdanto C++-ohjelmointiin luentomateriaali - Noppa

Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun
yliopisto
812347A Olio-ohjelmointi: Johdanto ohjelmointiin C++:lla
Olio-ohjelmointi – Johdanto ohjelmointiin C++-kielellä
Tässä osassa tutustutaan C++ - ohjelmoinnin perusrakenteisiin ja siihen, kuinka käyttäjän kanssa
kommunikoidaan käyttäen hyväksi konsoli-ikkunaa. Teksti perustuu pääasiallisesti
opetusmonisteen [CppTut] ensimmäiseen lukuun. Lukijan oletetaan tuntevan C-kielen siinä
määrin kuin se esitetään kurssilla Johdatus ohjelmointiin. C++ perustuu syntaksiltaan Cohjelmointikieleen: Lähes mikä tahansa C-ohjelma on myös C++-ohjelma. Tällä kurssilla
pyritään kuitenkin ohjelmoimaan C++:n luonteen mukaisesti, jolloin monien C-kielestä tuttujen
toimintatapojen tilalle tulee uusia menetelmiä.
1. C++ -pääohjelma
Ohjelman suoritus alkaa aina pääohjelmasta, joka on sekä C- että C++ - ohjelmissa nimeltään
main. Pääohjelma on tavallinen C++ -ohjelman funktio, jota käyttöjärjestelmä kutsuu
automaattisesti. Vaikka pääohjelma onkin normaali funktio, sitä erottavat tavallisista funktioista
seuraavat seikat:
1. Sitä ei voi kutsua erikseen.
2. Sen osoitetta ei voi tallentaa osoitinmuuttujaan.
3. Sitä ei tarvitse esitellä ennen määritystä.
4. Sitä voi olla vain yksi kappale ohjelmassa.
C++ -standardin mukaan pääohjelma voi olla kahta muotoa:
(1a) int main()
(1b) int main(void)
(2a) int main(int argc, char **argv)
(2b) int main(int argc, char *[]argv)
(2c) int main(int argc, char [][]argv)
Näistä kaksi ensimmäistä ovat ns. parametrittomia ja kolme viimeistä ns. parametrillisia muotoja
pääohjelmasta. Kaksi ensimmäistä muotoa ovat vaihtoehtoisia keskenään ja kolme viimeistä ovat
vaihtoehtoisia keskenään. Yleensä C++ -ohjelmissa käytetään muotoa 1a silloin, kun ei tarvita
käynnistysparametreja, ja muotoa 2a, kun tarvitaan syöttöparametreja. C++ -pääohjelman
paluuarvon tyyppi on aina int.
C++ -standardi ei määrittele paluuarvolle merkitystä; siinä ainoastaan luvataan arvon nolla
merkitsevän, että ohjelman suoritus on loppunut onnistuneesti. Muut arvot ilmaisevat
jonkinlaista virhetilannetta, jonka käyttöjärjestelmä tulkitsee. Paluuarvoa käytetään lähinnä
ohjelmissa, jotka on tarkoitettu putkitettavaksi käyttöjärjestelmätasolla siten, että edellisen
ohjelman tulostetieto on seuraavan ohjelman syöttötieto. Käyttöjärjestelmä päättää paluuarvon
perusteella jatketaanko putken suorittamista vai keskeytetäänkö se. C++ -standardissa on
määritelty kaksi symbolista vakiota, joita voi käyttää paluuarvoina. Nämä vakiot on määritelty
otsikkotiedostossa <cstdlib>:
1. EXIT_FAILURE tarkoittaa, että ohjelman suoritus on tavalla tai toisella epäonnistunut ja
2. EXIT_SUCCESS tarkoittaa, että ohjelman suoritus on onnistunut.
C++ -ohjelmalle voidaan välittää ohjelmaan käynnistämisen yhteydessä parametreja, kun
käytetään aiemmin mainittua toista pääohjelman muotoa. Käyttöjärjestelmä välittää parametrit
1
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun
yliopisto
812347A Olio-ohjelmointi: Johdanto ohjelmointiin C++:lla
C-tyyppisinä merkkijonoina, ts. merkkitaulukkoina, joiden viimeisenä merkkinä on nollamerkki.
Ensimmäinen parametri ilmoittaa, kuinka monta parametria ohjelmalle välitettiin. Tämä arvo on
aina suurempi kuin nolla, koska käyttöjärjestelmä välittää tavallisesti ohjelman nimen
ensimmäisenä parametrina. Ellei ohjelman nimeä välitetä, parametritaulukon ensimmäisenä
alkiona on tyhjä merkkijono (””). Oletetaan, että ohjelma nimeltä ”echo” on käynnistetty
seuraavasti:
echo hello
Tällöin parametrien arvot ovat:
 argc – muuttujan arvo on 2.
 Ensimmäisessä argv – taulukon alkiossa (argv[0]) on osoitin merkkijonoon ”echo”.
 Toisessa argv – taulukon alkiossa (argv[1]) on osoitin merkkijonoon ”hello”.
Luonnollisesti parametrien nimet voivat olla mitä tahansa, mutta niiden tyyppien pitää olla em.
eli int ja char** (tai char*[] tai char[][]). Yleisesti kuitenkin käytetään nimiä argc (eli argument
count) ja argv (eli argument values).
2. Lukeminen ja tulostaminen
C++-kielessä tulostamiseen käytetään operaattoria <<, jota kutsutaankin nykyään pääsääntöisesti
tulostusoperaattoriksi. (Operaattorin toinen merkitys on kokonaislukujen bittien siirtäminen
vasemmalle). Operaattori on määritelty standardikirjastossa jokaiselle C++ -kielen sisäiselle
tyypille. Kirjaston yksi suunnitteluperiaate on se, että ohjelmoijan omien tyyppien tulostaminen
ei eroa kielen sisäisten tyyppien tulostamisesta. Ohjelmoija voi siten määritellä
tulostusoperaattorin omille tyypeilleen haluamallaan tavalla. Tähän asiaan tutustutaan kurssilla
käsiteltäessä ylikuormittamista.
C++ - standardikirjastossa on määritelty cout -niminen oliomuuttuja, joka huolehtii
tulostamisesta konsoli-ikkunaan. Seuraava esimerkkiohjelma tulostaa tekstin ”Terve
maailma!” ja rivivaihdon.
#include <iostream>
int main()
{
std::cout << ”Terve maailma!” << std::endl;
return 0;
}
Huomioidaan ohjelmasta seuraavaa: Lähdekoodiin liitetään #include-direktiiveillä
standardikirjaston otsikkotiedostoja, joissa esitettyjä tyyppejä ja funktioita ohjelmassa tarvitaan.
Esimerkkiohjelmassa tällaisia ovat cout-oliomuuttujan ja operaattorin << esittelyt. Tulostettava
teksti on ohjelmassa esitetty merkkijonovakiona. Tällaisesta ohjelmakoodissa esiintyvästä
vakiosta käytetään myös nimitystä literaali. Literaali voi olla muunkin tyyppinen vakio kuin
merkkijono.
Merkintä std:: entiteettien cout ja endl edessä tarkoittaa sitä, että nämä entiteetit on määritelty
standardikirjaston nimiavaruudessa std. Nimiavaruuksien avulla voidaan välttää samannimisten
entiteettien esiintymiset ohjelmassa. Tällä kurssilla käytetään ainoastaan standardinimiavaruutta
2
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun
yliopisto
812347A Olio-ohjelmointi: Johdanto ohjelmointiin C++:lla
std. Merkinnän käyttäminen voidaan tehdä tarpeettomaksi esimerkiksi käyttämällä usingdirektiiviä (using directive), jonka avulla nimiavaruudesta tuodaan kaikki entiteetit.
Esimerkkiohjelma voidaan täten kirjoittaa myös muodossa:
#include <iostream>
int main()
{
using namespace std;
cout << ”Terve maailma!” << endl;
return 0;
}
Voidaan myös käyttää using-esittelyä (using declaration), jolloin valitaan, mitkä entiteetit
halutaan tuoda valitusta nimiavaruudesta. Esimerkkiohjelmasta tulisi tällöin:
#include <iostream>
int main()
{
using std::cout;
using std::endl;
cout << ”Terve maailma!” << endl;
return 0;
}
Siirrettävyyden ja ylläpidettävyyden vuoksi using-lauseiden näkyvyys tulisi rajata
mahdollisimman suppeaksi. Yleensä ne tulisi sijoittaa funktioiden toteutuksiin. Edellisissä
esimerkeissä ne on kirjoitettu main-funktioon.
Esimerkkiohjelmasta huomataan myös, että tulostettavia arvoja voidaan ketjuttaa yhteen
tulostuslauseeseen lisäämällä tulostettavien arvojen väliin operaattori <<.
Tulostuksen muotoilu: minimileveys, täytemerkki ja tasaus
C++-ohjelmassa voidaan tulostuksen muotoa muuttaa. C++:ssa tulostus on osittain
kenttäpohjaista, toisin sanoen tulostettavalle arvolle voidaan määritellä kentän pituus. Tämä
pituus on ainoastaan minimipituus: jos tulostettava arvo vie enemmän tilaa kuin kentän nykyinen
leveys, kentän leveyttä kasvatetaan. Lisäksi kentän minimipituus asetetaan jokaisen arvon
tulostamisen jälkeen nollaksi, mikä tarkoittaa sitä että jokaisen tulostettavan arvon kohdalla
pitää, ohjelmoijan niin halutessa, määritellä kentän minimileveys.
Kentän leveys voidaan määritellä ns. manipulaattorin avulla, joka asettaa tulostusvirran
muotoilulippuun tietyn arvon. Standardimanipulaattorit on esitelty kahdessa otsikkotiedostossa:
 Otsikkotiedostossa <iostream> on esitelty ne manipulaattorit, jotka eivät ota parametreja,
kuten endl.
 Otsikkotiedostossa <iomanip> on esitelty ne manipulaattorit, joilla on parametri, kuten
minimileveyden asettamiseen käytettävä setw.
Seuraavassa ohjelmassa käytetään manipulaattoreita setw, setfill, left ja right muotoilemaan
tulostusta:
3
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun
yliopisto
812347A Olio-ohjelmointi: Johdanto ohjelmointiin C++:lla
#include <iostream>
#include <iomanip>
int main()
{
using namespace std;
cout << ”C++” << ” ” << ”C++” << endl;
cout << setw(5) << ”C++” << ” ” << ”C++” << endl;
cout << setw(5) << setfill(’*’) << ”C++”
<< ” ” << ”C++” << endl;
cout << ”C++” << ” ” << ”C++” << endl;
cout << setw(5) << setfill(’*’) << left << ”C++”
<< ” ” << ”C++” << endl;
cout << setw(5) << setfill(’*’) << right << ”C++”
<< ” ” << setw(5) << ”C++” << endl;
return 0;
}
Ohjelma tulostaa samat kuusi riviä eri tavoin muotoiltuna seuraavasti:
C++ C++
C++ C++
**C++ C++
C++ C++
C++** C++
**C++ **C++
Tulostuksesta ja koodista huomataan:
1. Oletusleveys on nolla merkkiä, joten tulostuskentät laajenevat automaattisesti.
2. Oletustasaus tehdään tulostuskentän oikeaan reunaan.
3. Asetettu tietokentän leveys nollataan jokaisen arvon tulostamisen jälkeen.
4. Täytemerkki ja tasaus säilyvät asettamisen jälkeen.
Yhden sanan lukeminen ja lukuoperaattori
Konsoli-ikkunasta lukeminen tapahtuu C++:n IOStream-kirjaston välityksellä, kuten konsoliikkunaan tulostaminenkin. Tässäkään tapauksessa ohjelmoijan omien tyyppien lukemisen ei
tulisi erota C++ - kielen sisäisten tyyppien lukemisesta. Tätä varten on ylikuormitettu operaattori
>>, joka tarkoittaa myös kokonaisluvun bittien siirtoa oikealle. Tätä operaattoria kutsutaankin
nykyään pääsääntöisesti lukuoperaattoriksi. IOStream-kirjastossa on määritelty lukuoperaattorit
kielen sisäisille tyypeille. Ko. kirjastossa on määritelty olio cin, jonka välityksellä konsolinäppäimistöltä syötettyjä tietoja luetaan.
Lukuoperaattori lukee luettavan tiedon tyypin perusteella niin monta merkkiä kuin tyypin
merkkiavaruuteen kuuluu, minkä jälkeen lukeminen loppuu ja merkit muutetaan ko. tyypin
arvoksi. Lukeminen on oletusarvoisesti puskuroitua. Luettaessa poistetaan ensimmäiseksi alussa
olevat valkomerkit (white space characters), joita ovat mm. rivinvaihto-, tabulaattori- ja
välilyöntimerkit. Jäljellä olevat merkit jäävät seuraavan lukuoperaation luettavaksi. Lukeminen
tapahtuu siis tietokentittäin. Jos lukemisen yhteydessä ei tallenneta yhtään merkkiä
merkkijonoon, asetetaan virhelippu. Tilannetta käsitellään lyhyesti tuonnempana.
Luettaessa merkkijonotyyppistä tietoa luettavien merkkien määrä voidaan rajata asettamalla
tietokentän leveys samalla tavalla kuin tulostettaessa. Jos luettavien merkkien lukumäärää ei ole
4
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun
yliopisto
812347A Olio-ohjelmointi: Johdanto ohjelmointiin C++:lla
asetettu, lopetetaan lukeminen ensimmäiseen valkomerkkiin, joka jää seuraavan
lukutoimenpiteen käsiteltäväksi. Luettavien merkkien lukumäärä pitää aina asettaa kun luetaan
merkkitaulukkoon tietoa, jotta lukuoperaatio ei kohdistuisi taulukon ulkopuolelle. Tarkastellaan
seuraavaa ohjelmaa:
#include <iostream>
#include <iomanip>
int main()
{
const int TAULUKON_KOKO = 12;
char hetu[TAULUKON_KOKO];
using namespace std;
cout << ”Anna HETU: ”;
cin >> setw(TAULUKON_KOKO) >> hetu;
cout << ”Syötit seuraavan HETU:n ” << hetu << endl;
return 0;
}
Jos esimerkissä ei olisi asetettu tietokentän leveyttä ja käyttäjä olisikin syöttänyt viisitoista
merkkiä, niin kolme viimeistä merkkiä ja merkkijonon loppumisen ilmoittava nollamerkki olisi
kirjoitettu taulukon ulkopuolelle, mahdollisesti jopa ohjelmakoodin päälle. Tätä tilannetta
nimitetään puskurin ylivuodoksi (buffer overflow), joka on yksi yleisimmistä virusten ja muiden
haittaohjelmien käyttämistä tietoturva-aukoista.
Kun käytetään C++-standardikirjastossa määriteltyä merkkijonoluokkaa string, ei tarvitse
välittää merkkijonon maksimipituudesta, jolloin edellä oleva esimerkki voidaan kirjoittaa
muodossa:
#include <iostream>
#include <string>
int main()
{
using namespace std;
cout << ”Anna HETU: ”;
string hetu;
cin >> hetu;
cout << ”Syötit seuraavan HETU:n ” << hetu << endl;
return 0;
}
C++:n merkkijonotyyppi varaa tarvittaessa automaattisesti lisää tilaa, kunnes merkkijonon
maksimipituus täyttyy; tämä on yleensä sidoksissa int-tyypin kokoon, jolloin 32-bittissä
järjestelmissä tämä maksimikoko on 2^32 – 1 eli 4294967295 merkkiä, ts. noin 4 gigatavua
(olettaen että laitteistossa on niin paljon vapaata muistitilaa). Tämän vuoksi C++-ohjelmissa ei
juurikaan käytetä C-tyyppisiä merkkijonoja eli merkkitaulukkoja; tämä karsii koodista yhden
5
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun
yliopisto
812347A Olio-ohjelmointi: Johdanto ohjelmointiin C++:lla
mahdollisen virhepaikan. Haluttaessa voidaan luettavien merkkien lukumäärää rajoittaa myös
string-oliota käytettäessä asettamalla tietokentän leveys manipulaattorin setw avulla.
Syöttö- ja tulostusvirroilla (cin ja cout) on kolmesta lipusta (eli yksittäisestä bitistä) koostuva tila,
joka kertoo, onko virta toimintakykyinen. Nämä bitit ovat eofbit, badbit ja failbit. Kun muuttujaa
luettaessa ajaudutaan virhetilanteeseen, muuttujan sisältö on määrittelemätön ja cin-olion jokin
virhelippu on asetettu. Tilaa voidaan lukea käyttämällä seuraavia metodeita, jotka kaikki
palauttavat bool-tyyppisen arvon:
Metodi
good()
fail()
bad()
eof()
Selitys
Palauttaa arvon true, jos kaikki on OK.
Palauttaa arvon true, jos on tapahtunut lukuvirhe
Palauttaa arvon true, jos virta on käyttökelvoton.
Palauttaa arvon true, jos tiedoston loppumerkki on luettu.
Lukemisen yhteydessä riittää usein tarkastella tietovuon virhetilaa good()-metodin avulla:
#include <iostream>
#include <string>
int main()
{
using namespace std;
while (cin.good()) // Kutsutaan cin-olion metodia good()
{
string sana;
cout << ”Syötä tietoa: ”;
cin >> sana;
cout << sana << endl;
}
return 0;
}
Edellä oleva ohjelma lukee näppäimistöä, kunnes tapahtuu virhetilanne. Näppäimistöä
käytettäessä ainoa virhetilanne on se, että yritetään lukea ohi tiedoston loppumerkin, joka
saadaan aikaan Windows/DOS-ympäristössä painamalla yhtä aikaa ctrl ja Z ja Unix/Linuxympäristöissä painamalla yhtä aikaa ctrl ja D.
Jos virheellisessä tilanteessa yritetään lukea lisää tietoa, ei lukutoiminto johda mihinkään vaan se
epäonnistuu automaattisesti. Täten ennen jokaista lukutoimenpidettä tulisi tarkistaa ja korjata
virhetilanne poistamalla virhelippu kutsumalla tietovuon metodia clear.
6
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun
yliopisto
812347A Olio-ohjelmointi: Johdanto ohjelmointiin C++:lla
Numeerisen arvon lukeminen, tulostaminen ja tulostamisen muotoilu
Numeerisen arvon lukemiseen pätevät samat periaatteet kuin yhden sanan lukemiseen, paitsi että
asetettua tietokentän leveyttä ei huomioida eikä sitä nollata. Seuraavissa esimerkeissä oletetaan
että luettava tieto on kokonaislukutyyppi int.
Syöte
123
123%
123123
123.123
-123
+123
123123123123
Luettu arvo
123
123
123123
123
-123
123
Määrittelemätön
Taulukon toisessa tilanteessa lukutoiminto onnistuu, mutta tätä seuraava numeerisen tiedon
lukuyritys epäonnistuu, koska % - merkki ei ole minkään numeron osa, muuten se olisikin luettu
jo ensimmäisellä kerralla. Neljännessä tilanteessa lukeminen loppuu pistemerkkiin, koska se ei
osa merkistöä, joita käytetään kokonaislukujen yhteydessä. Viimeisessä tilanteessa tapahtuu
ylivuoto, koska syötettä ei voida esittää tyypin int avulla. Tällöin asetetaan cin-olion
virhetilalippu.
Kokonaislukujen tulkintaa voidaan ohjata myös kolmella manipulaattorilla:
 hex, jolloin lukujen kantalukuna on 16 ja käytettävä merkistö on ”+0123456789abcdefABCDEFxX”
 oct, jolloin lukujen kantalukuna on 8 ja käytettävä merkistö on ”+-01234567”
 dec, jolloin lukujen kantalukuna on 10 ja käytettävä merkistö on ”+-0123456789”
Jotta heksadesimaalimuodossa olevissa luvuissa voitaisiin käyttää x-kirjainta, sen edellä pitää
olla numero 0. Esimerkiksi 0x12 hyväksytään, mutta muotoa x12 ei hyväksytä. Yhdistelmä ”0x”
saa esiintyä vain kerran. Jos syötetty luku ei ala ”0x”, se tulkitaan alkavan sillä eli syöte ”12”
tulkitaan syötteeksi ”0x12”.
Oktaalilukujen alussa olevat numerot 0 tulkitaan yhdeksi nollaksi. Esimerkiksi 000012 tulkitaan
sen olevan 012. Jos syötetty luku ei ala numerolla 0, sen tulkitaan olevan kuitenkin alussa eli
syöte ”12” tulkitaan syötteeksi ”012”.
Kun luetaan reaalilukua, sallitut merkit ovat ”+-1234567890.eE”. Piste ja e-merkit voivat
esiintyä peräkkäin. Mikäli luvussa esiintyy E tai e-kirjain, luvussa täytyy esiintyä jokin numero
sekä ennen että jälkeen kirjaimen. Kirjainta seuraavan numeron edessä voi olla myös etumerkki.
Seuraavassa on esimerkkejä hyväksytyistä reaaliluvuista:
+.1
-1.23
1
1e5
1.e-5
1.23e2
0.342
.5
7
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun
yliopisto
812347A Olio-ohjelmointi: Johdanto ohjelmointiin C++:lla
Seuraavassa ohjelmassa kysytään käyttäjältä reaalilukuna lämpötila Celsius-asteina ja ohjelma
tulostaa lämpötilan Fahrenheit-asteikolla.
#include <iostream>
int main(){
using namespace std;
double celsius;
cout << ”Anna lämpötila celsius-asteina: ”;
cin >> celsius;
if (!cin.good())
{
cout << ”Luku ei kelpaa” << endl;
cin.clear();
}
else
{
double farenheit = 1.8 * celsius + 32;
cout
<<
<<
<<
<< celsius
” Celsius-astetta on Fahrenheit-asteikolla ”
farenheit
endl;
}
return 0;
}
Ohjelma havaitsee virheellisen syötteen, mutta ei pyydä uutta arvoa. Tällaisen rakenteen
toteuttamiseksi tulisi syöttövirrasta poistaa edellinen virheellinen syöte, kuten myöhemmin
näytetään.
Numeeriset arvot tulostetaan täsmälleen samoin kuin merkkijonot eli käyttämällä
tulostusoperaattoria, kuten edellä olevassa esimerkissä näytettiin. Kun ohjelmassa käytetään
reaalilukuja, niin usein halutaan muotoilla lukujen tulostusta esimerkiksi rajoittamalla
tulostettavien desimaalien määrää. Lukujen muotoilua varten otsikkotiedostoissa <ios> ja
<iomanip> on määritelty mm. seuraavat manipulaattorit:
Manipulaattori
showpos
noshowpos
left
right
setw(leveys)
fixed
scientific
setprecision(luku)
showpoint
Tarkoitus
Positiivisten lukujen yhteyteen tulostetaan + merkki
Positiivisten lukujen yhteydessä ei tulosteta merkkiä (oletus)
Tasataan luku vasempaan reunaan tulostuskenttää
Tasataan luku oikeaan reunaan tulostuskenttää (oletus)
Asetetaan tulostuskentän minimileveys (oletuksena 0)
Käytetään reaalilukujen yhteydessä desimaalimuotoa
Käytetään reaalilukujen yhteydessä tieteellistä muotoa (E)
Reaaliluvun merkitsevien numeroiden lukumäärä
Tulostetaan aina reaalilukujen yhteydessä desimaalipiste
Muut manipulaattorit löytää esimerkiksi Cppreference-sivustolta osoitteesta
http://en.cppreference.com/w/cpp/io/manip.
8
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun
yliopisto
812347A Olio-ohjelmointi: Johdanto ohjelmointiin C++:lla
Seuraavassa taulukossa on esimerkkejä reaaliluvun muotoilulippujen vaikutuksesta:
normaali
showpoint
fixed
scientific
setprecision()
2
6
2
6
2
6
2
6
421.0
4.2e+02
421
4.2e+02
421.000
421.00
421.000000
4.21e+02
4.210000e+02
0.0123456789
0.012
0.01234567
0.012
0.01234567
0.01
0.0123456
1.23e-02
1.2345678e-02
Ainoastaan tulostuskentän leveys nollataan jokaisen luvun yhteydessä, muut muotoilulippujen
asetukset säilyvät ensimmäisen asetuksen jälkeen.
Yhden rivin lukeminen
Edellä esitetyt lukutoimenpiteet olivat kaikki ns. muotoiltuja lukutoimenpiteitä, joissa
lukemiseen vaikuttavat muotoiluliput kuten esimerkiksi tietokentän leveys. Muotoiltujen
lukutoimintojen lisäksi IOStream-kirjasto määrittelee metodeja ja funktioita, joiden avulla
voidaan lukea merkkityyppistä tietoa raakadatana, ilman että siihen kohdistetaan mitään
muotoilutoimenpiteitä.
Käytetyin muotoilemattoman lukemisen funktion on luokalle string määritelty funktio getline,
jonka prototyyppi on seuraava:
std::istream& getline(std::istream& is, std::string& str, char delim = ’\n’);
Funktio palauttaa ensimmäisen parametrin. Kolmas parametri voidaan jättää pois, jolloin
lopettamismerkkinä on rivinvaihtomerkki (’\n’).
Funktio toimii seuraavasti:
1. Tyhjennetään parametrina annettu merkkijono.
2. Luetaan silmukassa annetusta vuosta merkkejä ja lisätään ne merkkijonoon, kunnes
tapahtuu jokin seuraavista:
a. Luetaan tiedoston loppumerkki, jolloin eof-metodi palauttaa arvon true,
b. Luettu merkki oli annettu lopetusmerkki (delim), joka poistetaan vuosta mutta ei
lisätä merkkijonoon, tai
c. Merkkijonon maksimipituus on saavutettu, jolloin fail-metodi palauttaa arvon
true.
Jos yhtään merkkiä ei luettu, asetetaan lippu fail, jolloin tietovuon metodi fail() palauttaa arvon
true. Funktiota käytetään seuraavasti:
9
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun
yliopisto
812347A Olio-ohjelmointi: Johdanto ohjelmointiin C++:lla
#include <iostream>
#include <string>
int main()
{
using namespace std;
cout << ”Anna etunimesi välilyönnillä erotettuna: ”;
string etunimet;
getline(cin, etunimet);
return 0;
}
Kun toteutetaan konsolipohjaista käyttöliittymää getline-funktiota käyttämällä, niin ennen
funktion kutsua pitää poistaa lukuoperaattorin mahdollisesti jäljelle jättämä rivinvaihtomerkki.
Tämä tapahtuu kutsumalla istream-luokan metodia ignore, jolla on seuraavat muodot:
std::istream& ignore();
std::istream& ignore(int lkm);
std::istream& ignore(int lkm, int delim);
Näistä ensimmäinen poistaa yhden merkin merkkipuskurista. Toinen poistaa annetun lukumäärän
verran merkkejä merkkipuskurista ja kolmas poistaa annettuun lopetusmerkkiin saakka
korkeintaan annetun lukumäärän verran merkkejä. Jos lukumäärä on yhtä suuri kuin suurin
mahdollinen kokonaisluku1, niin silloin poistetaan kaikki merkit kunnes luetaan annettu
lopetusmerkki tai tiedoston loppumerkki. Jos puskuri tyhjenee täysin, jäädään odottamaan lisää
merkkejä tuhottavaksi. Konsolipohjaisessa käyttöliittymässä kannattaa käyttää seuraavaa
rakennetta:
int luku;
cin >> luku;
cin.clear();
cin.ignore(numeric_limits<int>::max(), ’\n’);
string rivi;
getline(cin, rivi);
1
saadaan kutsumalla funktiota std::numeric_limits<int>::max(), joka on määritelty otsikkotiedostossa <limits>
10
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun
yliopisto
812347A Olio-ohjelmointi: Johdanto ohjelmointiin C++:lla
3. Merkkijonon muuttaminen numeeriseksi arvoksi ja päinvastoin
Kuten edellä on tullut ilmi, lukujen lukeminen virheettömästi käyttämällä konsolipohjaista
käyttöliittymää on varsin hankalaa. Yksi tapa tämän hankaluuden voittamiseksi on lukea kaikki
tiedot konsolilta merkkijonoina ja muuttaa ne tarvittaessa luvuiksi käyttäen hyväksi muotoiltua
lukutoimenpiteitä merkkijonoon. Tämä onnistuu käyttämällä vuota std::stringstream, joka on
määritelty otsikkotiedostossa <sstream>. Seuraavassa esimerkissä vuota käytetään lukemaan
kaksi kokonaislukua:
#include
#include
#include
#include
<iostream>
<iomanip>
<string>
<sstream>
int main()
{
using namespace std;
int luku1 = 0;
int luku2 = 0;
cout << ”Anna ensimmäinen luku: ”;
string luku;
getline(cin, luku);
if (cin.good())
{
stringtream strm1(luku);
strm1 >> luku1;
}
cout << ”Anna toinen luku: ”;
getline(cin, luku);
if (cin.good())
{
stringstream strm2(luku);
strm2 >> luku2;
}
double keskiarvo = (luku1 + luku2) / 2.0;
cout << ”Lukujen keskiarvo on ” << setprecision(2)
<< showpoint << keskiarvo;
return 0;
}
11
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun
yliopisto
812347A Olio-ohjelmointi: Johdanto ohjelmointiin C++:lla
Tätä merkkijonovuota voidaan myös käyttää muuttamaan numeerinen tieto merkkijonoksi:
#include
#include
#include
#include
<iostream>
<iomanip>
<string>
<sstream>
int main()
{
using namespace std;
int luku1 = 0;
int luku2 = 0;
cout << ”Anna ensimmäinen luku: ”;
string luku;
getline(cin, luku);
if (cin.good())
{
stringtream strm1(luku);
strm1 >> luku1;
}
cout << ”Anna toinen luku: ”;
getline(cin, luku);
if (cin.good())
{
stringtream strm2(luku);
strm2 >> luku2;
}
double keskiarvo = (luku1 + luku2) / 2.0;
stringstream strm3;
strm3 << setprecision(2)
<< showpoint << keskiarvo;
string str = strm3.str();
cout << ”Lukujen keskiarvo on ” << str;
return 0;
}
4. Funktiot
C++:ssa aliohjelmaa ja funktiota ei eroteta toisistaan; aliohjelmana voidaan pitää funktiota, jonka
paluutyyppi on void, ts. kyseessä on funktio, joka ei palauta mitään. Jotta funktiota voidaan
käyttää hyväksi eli kutsua, sen muodon on oltava selvillä siinä ohjelman kohdassa, jossa
funktiota kutsutaan. Tätä muodon selvittämistä kutsutaan funktion esittelyksi ja funktion
esittelylausetta funktion prototyypiksi.
Esittelylauseet voivat olla missä ohjelmakoodin kohdassa tahansa; esittelyt ovat voimassa
esittelylauseesta lähtien esittelylauseen sisältävän näkyvyysalueen loppuun asti. Yleensä
esittelylauseet kerätään yhteen, joko C++ tiedoston alkuun #include-lauseiden jälkeen tai
12
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun
yliopisto
812347A Olio-ohjelmointi: Johdanto ohjelmointiin C++:lla
otsikkotiedostoihin. Otsikkotiedostoja on käytettävä silloin, kun funktiota on tarkoitus kutsua
useasta eri tiedostosta.
Esittelyssä funktion nimeen liitetään sen palauttaman arvon tyyppi ja sen tarvitsemat parametrien
tyypit ja mahdollisesti parametrien oletusarvot. C++ -ohjelmassa saa olla samannimisiä
funktioita, kunhan niiden parametriluettelot poikkeavat toisistaan. Tätä sanotaan funktion
ylikuormittamiseksi. Esittelylauseessa annetaan aina funktion paluuarvon tyyppi, funktion nimi
ja sulkeissa oleva parametriluettelo. Esittelylause päättyy puolipisteeseen. Parametriluettelossa
on lueteltu funktion tarvitsemat parametrit pilkulla erotettuna toisistaan. Jos funktiolla ei ole
yhtään parametria, parametriluettelo on tyhjä. Parametrista ilmoitetaan tyyppi ja mahdollisesti
parametrin nimi. Lisäksi parametrilla voi olla oletusarvo.
Esittelyssä riittää siis parametrien yhteydessä vain niiden tyyppi, mutta koska esittelylause on
ainoa kohta, jossa funktion käyttäjä voi saada tietoa funktiosta, kannattaa parametri nimetä sen
tarkoituksen mukaisesti ja sisällyttää nimi esittelylauseeseen. Parametreille voidaan antaa
oletusarvoja parametriluettelon lopusta lähtien, jolloin annettuja arvoja käytetään, ellei funktion
kutsussa parametria käytetä. Oletusparametrien avulla voidaan vähentää funktioiden
kuormittamisen tarvetta.
Seuraavat kaksi esimerkkifunktiota palauttavat double-tyyppisen arvon ja niillä on yksi doubletyyppinen parametri:
double convertToCelsius(double farenheit);
double convertToFarenheit(double celsius);
Seuraavassa esittelylausessa esitellään funktio nimeltään calculateDistance, joka palauttaa
double tyyppisen arvon. Funktiolla on kuusi double tyyppistä parametria, joista kolmella
viimeisellä on oletusarvo:
double calculateDistance(double x1, double y1, double z1,
double x0 = 0, double y0 = 0, double z0 = 0);
Mikäli funktiota kutsutaan vain kolmella parametrilla, käytetään funktiossa muuttujien x0, y0 ja
z0 arvoina nollaa.
Jotta funktio suorittaisi halutut toiminnot, se pitää määritellä. Samalla funktiolla saa olla vain
yksi määrittely, sen sijaan esittelylauseita voi olla useita. Kahden funktion katsotaan olevan
samoja jos niiden nimet ovat samoja ja parametriluettelo on sama. Kaksi samannimistä funktiota,
joilla on eri parametriluettelo, tulkitaan eri funktioiksi, jotka molemmat on määriteltävä erikseen.
Määrittelylause alkaa funktion esittelylauseella ilman lopettavaa puolipistettä, jonka tilalle tulee
funktion toiminnat toteuttava määrittelylohko. Määrittelylauseet sijoitetaan aina johonkin C++kooditiedostoon. Määrittelylauseen parametriluettelossa pitää olla kaikilla käytettävillä
parametreilla nimi. Määrittelyn yhteydessä parametreille ei saa antaa oletusarvoa.
13
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun
yliopisto
812347A Olio-ohjelmointi: Johdanto ohjelmointiin C++:lla
Seuraavassa esimerkissä määritellään edellä esitellyt kolme funktiota:
#include <cmath>
// sqrt – funktio
double convertToCelsius(double farenheit)
{
return 5 * ( farenheit – 32 ) / 9;
}
double convertToFarenheit(double celsius)
{
return 32 + 9 * celsius / 5;
}
double calculateDistance(double x1, double y1, double z1,
double x0, double y0, double z0)
{
using std::sqrt;
double distance = (x1 – x0) * (x1 – x0);
distance += (y1 – y0) * (y1 – y0);
distance += (z1 – z0) * (z1 – z0);
distance = sqrt(distance);
return distance;
}
Lisäksi on mahdollista määritellä funktioita, joilla voi olla vaihtuva määrä parametreja, mutta
koska C++:ssa on mahdollista kuormittaa funktion nimeä eri parametriluetteloilla, niin tällaisia
funktioita ei enää C++:ssa juuri käytetä. Tekniikka on kääntäjäriippuvainen mutta rajapinnat on
standardoitu otsikkotiedostossa <cstdarg>.
Tärkeimpiä asioita funktioiden toiminnan ymmärtämisessä ja toteuttamisessa on mieltää, miten
funktiolle välitetään sen tarvitsemat parametrit. C++-kielessä parametrit voidaan välittää joko
arvo- tai viittausparametreina.
Funktion arvoparametrit
Arvotyyppisessä parametrien välityksessä parametriksi annettu muuttujan arvo kopioidaan
määrityksen yhteydessä olevaan muuttujaan. Tämä on parametrinvälityksen oletusmuoto
C++:ssa. Tällöin siis alkuperäisen muuttujan arvo ei muutu, vaikka funktiolle annettua arvoa
muutetaan funktiossa. Seuraavassa esimerkissä funktiossa muutetaan annettua parametria, joka
on sille annettu käyttäen arvoparametria:
14
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun
yliopisto
812347A Olio-ohjelmointi: Johdanto ohjelmointiin C++:lla
#include <iostream>
void muuta(int arvo);
int main()
{
using namespace std;
int a = 10;
cout << ”a (” << hex << &a << ”) = ” << dec << a << endl;
muuta(a);
cout << ”a (” << hex << &a << ”) = ” << dec << a << endl;
return 0;
}
void muuta(int arvo)
{
using namespace std;
cout << ”arvo (” << hex << &arvo << ”) = ”
<< dec << arvo << endl;
arvo = 123;
cout << ”arvo (” << hex << &arvo << ”) = ”
<< dec << arvo << endl;
}
Ohjelma voi tulostaa esimerkiksi seuraavaa (muuttujien muistiosoitteet voivat vaihtua):
a (0xfedc3823) = 10
arvo (0xfedc3900) = 10
arvo (0xfedc3900)= 123
a (0xfedc3823)= 10
Tulostuksesta nähdään että pääohjelman muuttujan arvo ei muutu ja että parametrimuuttujan
muistiosoite on eri kuin pääohjelman muuttujan.
Funktion viiteparametrit
Jos halutaan että funktiossa muutettu arvo päivittyy myös parametrina annettuun muuttujaan,
tieto on syytä välittää käyttäen viitetyypistä parametrin välitystä. Tällöin ei välitetä funktiolle
muuttujan arvoa vaan sen osoite, jota kautta kutsuva funktio ja kutsuttava funktio jakavat saman
muistialueen ja jommassakummassa funktiossa tapahtuva muutos näkyy molemmissa
funktioissa. Kun käytetään viiteparametria, niin ei kopioida parametrina annettua muuttujaa
toiseen muuttujaan, mikä on tärkeää varsinkin silloin kun välitetään suuria olioita. Tästä syystä
C++:ssa on tapana välittää olioparametrit käyttäen viiteparametria, koska olioiden kopioiminen
voi olla erittäin paljon resursseja vievä toimenpide. Arvoparametrina välittäminen ei aina ole
edes mahdollista.
Kun käytetään viittaustyyppistä viiteparametria, ei tarvitse välittää eksplisiittisesti muuttujien
osoitteita, vaan osoitteiden välittämisen hoitaa kääntäjä. Viittaustyyppisiä muuttujia käytetään
täsmälleen samoin kuin normaaleja muuttujia. Viittaustyyppinen parametri merkitään
15
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun
yliopisto
812347A Olio-ohjelmointi: Johdanto ohjelmointiin C++:lla
parametriluettelossa parametrin tyypin yhteydessä olevalla & -merkillä. Seuraavassa esimerkissä
käytetään viittaustyyppistä parametrien välitystä:
#include <iostream>
void muuta(int& arvo);
int main()
{
using namespace std;
int a = 10;
cout << ”a (” << hex << &a << ”) = ” << dec << a << endl;
muuta(a);
cout << ”a (” << hex << &a << ”) = ” << dec << a << endl;
return 0;
}
void muuta(int& arvo)
{
using namespace std;
cout << ”arvo (” << hex << &arvo << ”) = ”
<< dec << arvo << endl;
arvo = 123;
cout << ”arvo (” << hex << &arvo << ”) = ”
<< dec << arvo << endl;
}
Ohjelman tulostus on seuraavan kaltainen (ainoastaan muistiosoitteet voivat vaihtua):
a (0xfedc3823) = 10
arvo (0xfedc3823) = 10
arvo (0xfedc3823)= 123
a (0xfedc3823)= 123
Usein halutaan, varsinkin olioita parametreina välitettäessä, välttää parametrin kopiointi, vaikka
parametrimuuttujaa ei haluttaisikaan muuttaa funktiossa. Tällöin käytetään viiteparametreja.
Jotta muuttujaa ei vahingossa muutettaisi funktiossa, voidaan funktiolta evätä oikeus muuttaa
parametrin arvoa välittämällä parametri vakioviittauksen avulla, kuten seuraavassa esimerkissä:
16
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun
yliopisto
812347A Olio-ohjelmointi: Johdanto ohjelmointiin C++:lla
#include <iostream>
#include <string>
std::ostream& print(const std::string& merkkijono);
std::ostream& printNL(const std::string& merkkijono);
int main()
{
printNL(”Terve maailma\n”);
return 0;
}
std::ostream& print(const std::string& str)
{
return std::cout << str;
}
std::ostream& printNL(const std::string& str)
{
return print(str) << std::endl;
}
Jos jommassakummassa funktiossa yritettäisiin muuttaa str-parametrin arvoa, kääntäjä antaisi
virheilmoituksen. Parametri välitetään siis vakiomuotoisena viiteparametrina eikä
arvoparametrina tehokkuussyistä, koska tällöin ei muodosteta str-muuttujaan kopiota
tulostettavasta merkkijonosta.
Osoittimet funktion parametreina
Kun funktion parametrina on osoitin, se välitetään käyttäen arvovälitystä. Osoitin viittaa
kuitenkin johonkin muistiosoitteeseen, joten sen kautta voidaan vaikuttaa samaan osoitteeseen
kuin alkuperäinen osoitinmuuttuja. Näin ollen osoittimia käyttämällä saa aikaan vaikutukseltaan
samantapaisen funktion kuin viiteparametreilla. Näissä kummassakin tapauksessa voidaan puhua
muuttujaparametreista, kuten teoksessa [Hie], s. 199.
Kun parametri välitetään muuttujaparametrina käyttäen osoitinta parametrina, ei funktiolle
annetakaan muuttujan arvoa vaan osoite, jossa muuttuja sijaitsee tietokoneen muistissa.
Jokaisella muuttujalla paitsi literaalilla on muistiosoite, myös vakiomuuttujilla. Muuttujan osoite
saadaan selville osoiteoperaattorin & avulla. Toinen osoitinmuuttujien yhteydessä käytettävä
operaattori on sisältöoperaattori *. Muistipaikan sisältöön viittaamista tämän operaattorin
avulla kutsutaan myös dereferenssiksi. Parametrien välityksessä osoiteoperaattoria käytetään
funktiokutsun yhteydessä, ja sisältöoperaattoria käytetään kutsuttavan funktion määrittelyssä,
kun halutaan lukea tai kirjoittaa parametrin arvoa. Parametriluettelossa osoitintyyppinen
parametri ilmaistaan liittämällä tarkoitetyyppiin * - merkki. Seuraavassa esimerkissä on
toteutettu edellisen esimerkin kanssa samalla tavoin toimiva ohjelma käyttäen osoitintyyppistä
muuttujaparametria:
17
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun
yliopisto
812347A Olio-ohjelmointi: Johdanto ohjelmointiin C++:lla
#include <iostream>
void muuta(int* arvo);
int main()
{
using namespace std;
int a = 10;
cout << ”a (” << hex << &a << ”) = ” << dec << a << endl;
muuta(&a); // Parametriksi muuttujan a muistiosoite
cout << ”a (” << hex << &a << ”) = ” << dec << a << endl;
return 0;
}
void muuta(int* arvo)
{
using namespace std;
cout << ”arvo (” << hex << arvo << ”) = ”
<< dec << *arvo << endl;
*arvo = 123;
cout << ”arvo (” << hex << arvo << ”) = ”
<< dec << *arvo << endl;
}
Ohjelman tulostus on seuraava (jälleen ainoastaan muistiosoitteet voivat vaihtua):
a (0xfedc3823) = 10
arvo (0xfedc3823) = 10
arvo (0xfedc3823)= 123
a (0xfedc3823)= 123
Kumpaa muuttujaparametrien välitystapaa käyttää, on lähes puhtaasti tyylikysymys, mutta
osoitemuuttujat ja viittausmuuttujat eroavat toisistaan seuraavasti:
 Viittausmuuttujaa käytettäessä pitää olla olemassa jokin muuttuja, johon viittausmuuttuja
kiinnitetään lopullisesti.
 Viittausmuuttujan kohdetta ei voi vaihtaa määrityksen jälkeen.
 Osoitinmuuttujan määrityksen yhteydessä osoittimen arvon voi asettaa nollaksi, toisin
sanoen osoitettavaa muuttujaa ei tarvitse olla olemassa.
 Osoitinmuuttuja voidaan muuttaa osoittamaan toiseen muuttujaan myös määrityksen
jälkeen.
Funktion paluuarvo
Funktion paluuarvo voi olla jokin seuraavista:
 void-tyyppi, jolloin funktio ei palauta mitään (funktio on aliohjelma),
 tavallinen tyyppi, jolloin paluuarvo kopioidaan,
 osoitintyyppi, jolloin välitetään palautettavan arvon sisältämän muuttujan osoite
 viittaustyyppi, jolloin välitetään muodostettu viittausmuuttuja, joka viittaa palautettavan
arvon sisältämän muuttujaan.
18
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun
yliopisto
812347A Olio-ohjelmointi: Johdanto ohjelmointiin C++:lla
Kahdessa viimeisessä tavassa on yksi C++-kielen Akilleen kantapäistä. Se muuttuja, johon
viitataan, on voitu tuhota, kun funktiosta palataan sitä kutsuvaan funktioon. Tällainen tilanne voi
syntyä varsinkin, kun palautettava viite on viittaus parametrimuuttujaan tai paikalliseen
muuttujaan. Nykyaikaiset kääntäjät antavat kyseisissä tilanteissa varoituksia ja virheilmoituksia,
joten kannattaa tulkita tarkoin myös kääntäjän antamat varoitukset, nämäkin voivat johdattaa
ohjelmointivirheen jäljille.
Taulukkotyypin käyttö parametrina ja paluuarvon tyyppinä
C++:ssa on mahdollista käyttää ohjelmissa C-kielen mukanaan tuomaan taulukkotyyppiä, jonka
koko määritellään käännösaikana eikä sitä voida ohjelmallisesti muuttaa. Taulukon alkioiden
tyyppi voi olla mikä tahansa C++:n tyyppi, jolle on määritelty oletusmuodostin, myös
taulukkotyyppi, jolloin voidaan rakentaa moniulotteisia taulukoita. Oletusmuodostimen
käsitteeseen palataan perehdyttäessä oliopohjaiseen ohjelmointiin.
Taulukkomuuttuja määritellään muuttujan perään lisättävillä hakasulkeilla, joiden sisällä
ilmaistaan taulukon koko. Tätä kokoa ei ole mahdollista ohjelmallisesti selvittää, joten
taulukon koko on aina välitettävä taulukkoa käyttäville funktioille. Taulukon nimi on samalla
osoitin ensimmäiseen alkioon. Taulukko välitetään funktioille osoittimena taulukon
ensimmäiseen alkioon. Vastaavasti, haluttaessa palauttaa taulukko, paluutyypin arvona on osoitin
taulukon ensimmäiseen alkioon.
Seuraavassa on esitelty funktiot, joiden ensimmäisenä parametrina on taulukkotyyppi ja toisena
tyyppinä taulukon alkioiden lukumäärä. Ensimmäinen funktio palauttaa luettujen alkioiden
lukumäärän ja toinen funktio ei palauta mitään.
int lueLampotilat(double * lampotilat, int koko);
void tulostaLampotilat(double * lampotilat, int elementit);
19
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun
yliopisto
812347A Olio-ohjelmointi: Johdanto ohjelmointiin C++:lla
Niiden toteutukset voivat olla esimerkiksi:
int lueLampotilat(double * lampotilat, int koko)
{
cin.clear();
int count = 0;
while (cin.good() && count < koko){
cout << ”Anna lämpötila: ”;
double arvo;
cin >> arvo;
if (cin.good()) // jos lukeminen onnistui
{
lampotilat[count] = arvo;
++count;
}
}
if (!cin.good())
return -1;
return count;
}
void tulostaLampotilat(double * lampotilat, int elementit)
{
for (int i = 0; i < elementit; ++i)
cout << i + 1 ”. kuukausi: ” << lampotilat[i] << endl;
}
Funktioita voitaisiin käyttää pääohjelmassa esimerkiksi seuraavasti:
int main()
{
const int TAULU_KOKO = 12;
double taulu[TAULU_KOKO];
int lkm = lueLampotilat(taulu, TAULU_KOKO);
tulostaLampotilat(taulu, lkm);
return 0;
}
Olioita sisältäviin taulukoihin palataan myöhemmin.
Lähteet
[CppTut] Juustila, A. Kettunen, H. Kilpi, T. Räisänen, T. Vesanen A.: C++ -tutoriaali
(Opetusmoniste) 2004, Saatavana http://herkules.oulu.fi/isbn9514279425/
[Hie] Hietanen, P.: C++ ja olio-ohjelmointi, 3. laitos, Docendo 2004
20