Oliot ja luokat
Oliot ja luokat
Tervetuloa t�m�n oppaan ehdottomasti t�rkeimm�n osion pariin. Kuten ehk� oletkin jo huomannut, rakastan suuresti k�yt�nn�n tilanteiden pohtimista C++:n avulla. Ja niit� on tulossa viel� paljon lis��, joten ei auta kuin tottua. Nyt menn��n taas: Koska vartiointifirma Pamppu ja sateenvarjo oy:lle tekem�si ohjelma toimi loistavasti ja moitteettomasti, p��tt�v�t he tilata sinulta toisenkin ohjelman. Ohjelman pit�isi yll�pit�� heid�n asiakasluetteloaan. Jokaisella asiakkaalla on tietoina nimi, osoite, sovittu kuukausimaksu jne..
Kuinka t�llainen tietom��r� pit�isi j�rjest��? No, hommahan on helppo. Tehd��n taulukko nimi[ASIAKKAIDEN_LKM], maksu[ASIAKKAIDEN_LKM] jne.. N�in se onnistuisi ihan perusominaisuuksien avulla. Mutta mites suu pannaan, kun noin puolet asiakkaista haluaa y�vartioinnin ja toinen puoli ei? Kaiken kukkuraksi y�vartijoiden m��r� vaihtelee. Yksi ratkaisu on tehd� taulukko yovartijat[ASIAKKAIDEN_LKM] ja olla v�litt�m�tt� siit�, ett� osa alkioista on tyhji� ja turhia. Jos ei viel� hymy hyydy, lis��mme soppaan sen, ett� ohjelman pit�isi pysty� lis��m��n ja poistamaan asiakkaita lennossa. Ohjelmaa py�ritt�� vanha 386-kone, jossa on v�h�n muistia ja tehoa. Niinp� muistia tai koneen prosessoriaikaa ei saisi hukata ollenkaan.
Kuulen jo kuinka huudatte edellisen esimerkin ikeen alla: "Me haluamme olioita ja luokkia! Tahtoo!" Hyv� on, nyt niit� tulee. Pient� jaarittelua kuitenkin viel�.
Lapsikin huomaa, ett� edellisen esimerkkiongelman ratkaisu taulukoilla ontuu. Asiakas olisi parempi pit�� omana kokonaisuutenaan, ei vain yhten� indeksin� moniin taulukoihin. Pelkill� taulukoilla ja isolla kasalla purkkavirititelmi� edellisen tapauksen voi kyll� hoitaa muistia ja tehoa haaskaamatta, mutta tulos on sellainen ohjelmanv�nkyr�, jota kukaan ei vapaaehtoisesti kyll� ala en�� kehitt�m��n. Niinp� k�sittelemmekin nyt luokat (class) ja oliot (object). T�m� on nyt vihdoinkin sit� C++:aa itse��n. C++ on oliopohjainen kieli, joten ilman olioita ei pitk�lle p�tki. Toki C++:lla voi ohjelmoida ilmankin olioita, mutta harvemmin syyt� sellaiseen on..
Ajatellaan nyt n�in p�in, ett� kaikkia asiakkaan tietoja ei laiteta taulukkoon, vaan ne s�il�t��n asiakkaaseen, ja sitten tehd��n asiakkaista taulukko. Me tarvitsemme asiakasolioita. Koska kaikki asiakkaat ovat samanlaisia, sit� y�vahtijuttua lukuunottamatta, ne ovat samoja olioita. Niill� on samat ominaisuudet, mutta eri tiedot. Samanlaiset oliot kuuluvat samaan luokkaan. Nyt otamme ja m��rittelemme luokan Asiakas.
class Asiakas { public: char* nimi; char* osoite; int kuukausiMaksu; }; // huomaa ; -merkki lohkon lopussa!!!
Ensin on varattu sanan class. Sitten seuraa luokan nimi. Sitten alkaa lohko, jossa luokka m��ritell��n. public tarkoittaa, ett� alla olevat muuttujat ovat kaikkien k�yt�ss�. TSM. Sen j�lkeen homma on tuttua, m��rittelemme vain Asiakkaaseen liittyv�t muuttujat. Lohko loppuu ja sitten tulee taas penteleen t�rke� kohta, nimitt�in puolipiste lohkon lopussa. Muista se! Lohkojen loppuun ei muuten tule C++:ssa puolipistett�, mutta luokan m��rittely on (mielest�ni typer�) poikkeus.
Kun haluamme luoda asiakkaan, k�y se n�in (kunhan luokka on ensin m��ritelty):
Asiakas erkinKumikorjaamo;
Luomme olion erkinKumikorjaamo. Asiakas on itseasiassa kuin mik� tahansa muuttujatyyppi. Voi tehd� Asiakas-taulukoita ja muiden luokkien j�senen� voi olla Asiakas-olioita. Palataksemme esimerkkiin, teemme n�in:
Asiakas asiakkaat[ASIAKKAIDEN_LKM];
Luomme siis taulukon asiakkaat, joka sis�lt�� ASIAKKAIDEN_LKM (vakio) kappaletta Asiakas-luokan olioita. Olion sis�lt�mi� muuttujia k�ytet��n piste-operaattorin avulla. Siis olionNimi.muuttuja.
Asiakas kalevinKukkakauppa; kalevinKukkakauppa.kuukausiMaksu = 150; // Kalevi maksaa 150 mk kuukaudessa
On t�rke�t� hahmottaa luokan ja olion ero. Luokka on kuin muotti. Se kertoo mit� ominaisuuksia olioilla on. Olio on taas yksi sill� muotilla tehty piparkakku. Sill� on luokan ominaisuudet, siis muuttujat, mutta niiss� omat arvonsa.
T�m� ei kuitenkaan ratkaise esimerkkimme kahta ongelmaa, y�vahtiasian eroavaisuutta ja asiakkaiden alati muuttuvaa lukum��r��. N�ihin asioihin tarvitaan kuitenkin osoittimia, dynaamista muistinhallintaa ja perint��. Ennen niit� sinun pit�� kuitenkin oppia luokkien perusteet, joten niit� ensin.
Luokka on muutakin kuin pelkk� kasa muuttujia. Luokkaan saa nimitt�in j�senfunktioita eli metodeja. Niiden kautta luokan olioita k�ytet��n. Seuraa lastenmielinen esimerkkiohjelma:
#include<iostream.h> class Veturi { public: int nopeus; void Puhu(); }; void Veturi::Puhu() { cout << "Tuut! Tuut! Hurjastelen " << nopeus << " km/h nopeutta!"; } int main() { Veturi villeVeturi; villeVeturi.nopeus = 50; villeVeturi.Puhu(); return EXIT_SUCCESS; }
Olio on tiedot ja toiminnallisuus
Keksitk� mit��n muuta tapaa toteuttaa Veturia matkiva ohjelma? On nimitt�in olemassa toinenkin vaihtoehto... Etk�? Eritt�in hyv�, koska se toinen vaihtoehto on v��r� ja tuo yll� esitetty oikea. Mietip�s mill� tavalla alla oleva muunnelma Veturista on huonompi:
#include<iostream.h> class Veturi { public: int nopeus; }; void Puhu(Veturi veturi) { cout << "Tuut! Tuut! Hurjastelen " << veturi.nopeus << " km/h nopeutta!"; } int main() { Veturi villeVeturi; villeVeturi.nopeus = 50; Puhu(villeVeturi); return EXIT_SUCCESS; }
Varmasti mieleesi tulee ainakin, ett� j�lkimm�inen vaihtoehto on ep�looginen. Kun vertaamme puhumista, on villeVeturi.Puhu() eritt�in looginen, sen voisi k��nt�� selkokieliseksi k�skyksi: "Ville Veturi! Puhu!". Puhu(villeVeturi) taas voisi vastata suomen kielen lausetta: "Suorita puheoperaatio Ville Veturilla". Kuulostaa v�h�n KELA:n lomakkeen t�ytt�ohjeilta...
Kuten huomaat, kun ohjelman metodit (j�senfunktiot) sijoitetaan samaan luokkaan ohjelman tietojen kanssa, saavutetaan kaksi etua: ohjelma vaikuttaa loogiselta eli on sellainen kuin terveell� maalaisj�rjell� sen kuvittelisi olevan, ja sen lis�ksi ohjelman l�hdekoodi on selke�mp��. Minusta ainakin veturin antaminen Puhu()-funktioille on hankalaa verrattuna luokan sis�ll� toteutettuun puhumiseen, jossa vastaavaa hankaluutta ei tarvitse tehd� - ollaanhan jo valmiiksi oikean olion "sis�ll�" (tuolle "sis�ll� olemiselle" on my�s teknisempi m��ritelm�, mihin palaamme my�hemmin). Ja nytp� olemmekin tunkeutuneet C++-ohjelmoinnin, ja olio-ohjelmoinnin yleens�, kaikkein pyhimp��n. Olio-ohjelmointi on sit�, ett� tiedot (muuttujat) ja toiminnallisuus (metodit) ovat yhdess�. Kaikki mit� t�m�n j�lkeen kerron ei ole l�hellek��n yht� t�rke�� kuin tuo edellinen havainto.
OLIO = TIEDOT + TOIMINNALLISUUS
Haluaisin viel� lis�t�, ett� tiedon ja toiminnallisuuden yhdist�minen ei
ole pelk�st��n loogista, vaan johtaa parempiin ohjelmiin. Kun luokkaan on
paketoitu sek� tiedot ett� toiminnot, syntyy "musta laatikko".
Luokan k�ytt�j�n ei tarvitse v�litt�� siit� miten luokka itseasiassa
toimii, kunhan se toimii oikein. Ohjelmoidessa voi hyvinkin tuntua, ett� p��n
sis�ll� on menossa italialaisen talonyhti�n kokous, siis kaaos. Juuri t�t�
varten olio-ohjelmoinnissa koitetaan piilottaa kaikki turha, joka muuten
kuluttaisi ohjelmoijan kallista mutta v�h�ist� (?) aivokapasiteettia.
Tiedon kapselointi
Tiedon kapselointi. Armottomana ihmisen� iskin sinua suoraan p�in kasvoja yhdell� hyvin t�rke�ll� oliopohjaisen ajattelun termill�. Viel� uudestaan: tiedon kapselointi. Idea on, ett� jokaiseen k�sitteeseen liittyv�t tiedot on kapseloitu siihen k�sitteeseen (olioon), eiv�tk� muut p��se niit� sorkkimaan. Tiedon kapselointi! Edellisess� esimerkiss� tietoa ei oltu kapseloitu, vaan se roikkui villin vapaana kuin Kemppaisen kolli kes�housuissaan.
Jos mietit edellista esimerkki�, niin huomaat ett� kuka tahansa p��see sorkkimaan luokan muuttujia. Mit� kapselointia ja suojaamista se on muka olevinaan? Nyt konkari vet�� takataskustaan taika-avaimen, nimitt�in varatun sanan private. private on publicin kaveri. Kun public m��rittelee julkisia j�senmuuttujia ja funktioita, niin private m��rittelee yksityisi�. Julkiset muodostavat olion k�ytt�liittym�n, tavan jolla muut ohjelman osat kommunikoivat olion kanssa. Yksityiset osat muodostavat luokan sis�isen toiminnan. Ne ovat muuttujia ja funktioita, joita luokka tarvitsee omaan sis�iseen toimintaansa ja joita ulkopuoliset eiv�t p��se sotkemaan. Yksityiseksi m��ritelty� tietoa voivat k�ytt�� vain luokan omat metodit, j�senmuuttujat. Luokan j�senet ovat oletuksena yksityisi�.
Esittelemme nyt lis�� h�yrykoneiden kauhuja.
#include<iostream.h> const int MAX_NOPEUS = 180; class Veturi { public: void AsetaNopeus(int n); void Kiihdyta(); void Jarruta(); int LueMittari(); // nk. saantifunktio private: int nopeus; }; void Veturi::AsetaNopeus(int n) { nopeus = n; } void Veturi::Kiihdyta() { nopeus += 20; if (nopeus > MAX_NOPEUS) nopeus = MAX_NOPEUS; } void Veturi::Jarruta() { nopeus -= 20; if (nopeus < 0) nopeus = 0; } int Veturi::LueMittari() { return nopeus; } int main() { Veturi villeVeturi; villeVeturi.AsetaNopeus(0); villeVeturi.Kiihdyta(); villeVeturi.Kiihdyta(); cout << "Nopeutta " << villeVeturi.LueMittari() << " km/h!" << endl; villeVeturi.Jarruta(); cout << "Nopeutta " << villeVeturi.LueMittari() << " km/h!" << endl; // villeVeturi.nopeus = 50; return EXIT_SUCCESS; }
Veturi osaa muuttaa nopeuttaan ja ilmoittaa sen. Nopeus asetetaan nollaksi olion luonnin yhteydess�. Ensin veturi kiihdytet��n 40 km/h nopeuteen ja nopeus tulostetaan. Sitten jarrutetaan ja nopeus ilmoitetaan uudestaan. Huomaa viimeisell� rivill� oleva viittaus yksityiseen muuttujaan. Jos sit� ei olisi kommentoitu, se ei olisi mennyt k��nt�j�st� l�pi. Yksityinen tieto ei ole ulkopuolisten k�ytett�viss�.
Nopeus on varma muuttuja. Jarruta() ja Kaasuta() metodit valvovat tehokkaasti nopeuden k�ytt��. Jos pelk��t, ett� muuttujien k�ytt� erillisten funktioiden kautta hidastaa ohjelmaa, niin h�ps�n p�ps�n. Hyv� k��nt�j� optimoi saantifunktiot olemattomiin. Huomaa kohta: k��nt�j� optimoi. Samalla k��nt�j� tarkistaa tietotyypit. Eli k��nt�j� tekee enemm�n ty�t�, mutta lopputulos (konekielinen ohjelma) on sama. Metodin AsetaNopeus(int) avulla nopeuden voi kuitenkin sotkea pahanp�iv�isesti. Ihannetilanne olisi semmoinen, jossa luokan k�ytt�j� ei pystyisi sotkemaan ollenkaan luokan j�senmuuttujien arvoja. Kun kirjoittaa v�h�n pitempi� ja monimutkaisempia ohjelmia, jaksaa t�llaist� ominaisuutta kyll� arvostaa... Niinp� pit�isi olla joku kikka, jolla nopeus voitaisiin alustaa olion luonnin yhteydess� ja AsetaNopeus(int) metodi tulisi tarpeettomaksi... n�en horisontissa h��m�tt�v�n aasinsillan...
Muodostin- ja tuhoajafunktiot
Usein oliota luodessa t�ytyy tehd� jotain toimia, kuten varata muistia luokan tiedoille. Ei kannata j�tt�� luokan k�ytt�j�n vastuulle kutsua homman hoitavaa funktiota, vaan k�ytt�� luokkaan liittyv�� muodostinfunktiota (constructor). Kun luokka luodaan, luo k��nt�j� sille automaattisesti muodostinfunktion - joka ei tee mit��n. Jos haluamme laittaa jotain toimia luokan tyyppisen olion luonnin yhteyteen, voimme korvata muodostinfuktion omallamme. Muodostinfunktion nimi on luokan nimi ja sill� ei ole paluuarvoa. Muodostinfunktio pit�� tietenkin m��ritell� julkiseksi.
#include<iostream.h> class Auto { public: Auto(); // muodostin }; Auto::Auto() { cout << "Muodostinta kutsuttu"; } int main() { Auto lada; return EXIT_SUCCESS; }
Muodostinfunktiolle voi my�s v�litt�� parametrej� ja siit� voi kirjoittaa monta erilaista versiota eri parametreill� - polymorfismi siis toimii t�ss�kin suhteessa. Muodostinfunktion parina toimii tuhoajafunktio (destructor). Sit� kutsutaan kun olio tuhotaan. Sen m��rittely on muuten vastaava, mutta funktion nimi alkaa ~ (tilde, mato)-merkill�. Jos m��rittelet oman muodostinfunktion, niin k��nt�j� j�tt�� oman tyhj�n muodostimensa m��rittelem�tt�. Siis jos m��rittelet yhden int-parametrin ottavan muodostimen, ei luokkaa voida en�� luoda ilman parametrej�, koska tyhj� parametriton muodostin j�� m��rittelem�tt�.
Rakennamme nyt luokan, jota voidaan k�ytt�� hyvin yksinkertaiseen tiedon kryptaamiseen, siis salaamiseen. Siin� on taulukollinen unsigned char muuttujia. Taulukko varataan dynaamisesti (dynaamisesta varauksesta kerron tarkemmin sitten my�hemmin), sen koko annetaan oliota luodessa. Jos kokoa ei anneta, k�ytet��n oletuskokoa. Tietenkin varattu muisti vapautetaan kun olio lakkaa olemasta. Itse kryptausosa on mukana l�hinn� aidon tunnelman saavuttamiseksi.
const int OLETUS_KOKO = 256; class KryptattuTaulukko { public: KryptattuTaulukko(); KryptattuTaulukko(int koko); ~KryptattuTaulukko(); void Koodaa(); unsigned char *taulukko; int taulukonKoko; }; KryptattuTaulukko::KryptattuTaulukko() { taulukko = new unsigned char[OLETUS_KOKO]; // varataan muisti taulukonKoko = OLETUS_KOKO; } KryptattuTaulukko::KryptattuTaulukko(int koko) { taulukko = new unsigned char[koko]; // varataan muisti taulukonKoko = koko; } KryptattuTaulukko::~KryptattuTaulukko() { delete [] taulukko; // vapautetaan muisti } void KryptattuTaulukko::Koodaa() { for (int i=0; i < taulukonKoko; i++) taulukko[i] = 255 ^ taulukko[i]; // py�r�ytet��n bitit ymp�ri }
Pari puutetta viel� yll� olevan ohjelman rakenteessa on, mutta ne eiv�t ole niin oleellisi� - viel�.
Muodostinfunktiossa muuttujien alustus voidaan hoitaa my�s itse funktion rungon ulkopuolella, t�h�n tyyliin:
KryptattuTaulukko::KryptattuTaulukko() : taulukonKoko(OLETUS_KOKO) { taulukko = new unsigned char[OLETUS_KOKO]; }
Siis muodostinfunktion nimen j�lkeen kaksoispiste, sitten muuttujien alustus. Muuttujan arvo annetaan suluissa, muuttujan nimen per�ss�. T�m� tapa on suositeltavampi kuin muodostinfunktion rungossa muuttujien alustaminen. Yll� oleva funktio on karsea esimerkki muodostinfunktiosta. Koska muodostinfunktio ei palauta arvoa, se ei voi viesti� onnistumisesta mitenk��n. Ja koska se varaa muistia, voi ongelmiakin tulla. Muodostinfunktiossa ei saa tehd� mit��n, mik� voi ep�onnistua (muistin varaus, tiedostojen avaus jne...). Joten luokan kehittely jatkuu, kunhan vaan ved�mme operaattorit ensin k�ytt��n.
Operaattoreiden ylikuormittaminen
Mik�li et ole viel� tutustunut String-luokkaan, niin kertoilenpa siit� t�ss� aluksi. Merkkijonojen ja String-luokan k�ytt� on tarkemmin esitelty standardikirjastojen yhteydess�. String luokka on kuin yhdistetty luokka ja taulukko. Toisaalta sill� on muodostin- ja tuhoajafunktiot, mutta toisaalta sit� voi k�ytt�� indeksointioperaattorilla kuin tavallista taulukkoa. Siis String-luokan olioita voi k�ytt�� n�in: merkkijono[3]. T�m� palauttaa merkkijonon nelj�nnen merkin. Vaikka String onkin k��nt�j�n mukana tuleva, ei siihen kuitenkaan liity mit��n maagisia, kuolevaisille saavuttamattomia temppuja. Pelk�st��n operaattoreiden ylikuormittamista, mik� kyll� tavallisille kuolevaisille voi vaikuttaa mystisten voimien ty�lt� - mutta ei C++-velholle!
Kun k��nt�j�n pit�� k�sitell� +-operaattori kahden int-luvun yhteydess�, ei se ole mik��n ongelma. Kai nyt jokainen pari lukua osaa yhteen ynn�t�. Mutta ent� jos k��nt�j�� k�sket��n ynn��m��n kaksi itse ohjelmoidun Tyontekija-luokan oliota? K��nt�j� ei tunne luokan ominaisuuksia, joten ainoa heti mieleen tuleva ratkaisu olisi laskea olioiden muistiosoitteet yhteen. Ratkaisu onkin mahdollinen, mutta j�rjeton. Muistiosoitteella ei ole mit��n tekemist� luokan toiminnan kanssa ja tulos osoittaisi mihin sattuu. Parempi vaihtoehto olisi vaikka laskea yhteen ty�ntekij�iden palkat. N�in �lyk�st� toimintaa emme kuitenkaan C++-k��nt�j�lt� voi odottaa, vaan meid�n pit�� se itse ohjelmoida.
Ensimm�inen ratkaisu olisi tehd� Tyontekija-luokkaan metodi Lisaa(Tyontekija&). Lisays tehtaisiin nain:
Tyontekija jamppa; Tyontekija jomppa; palkatYhteensa = jomppa.Lisaa(jamppa);
Tuo ei kuitenkaan ole hirve�n havainnollinen tapa. Me nimitt�in voimme ohjelmoida Tyontekija-luokkaan +-operaattorin, siis ylikuormittaa +-operaattorin. C++ antaa mahdollisuuden ylikuormittaa seuraavat operaattorit:
+ - * / % ^ & | ~ ! = < > += -= *= /= %= ^= &= |= << >> >>= <<= == != <= >= && || ++ -- ->* , -> [] () new delete
Ylikuormitettu operaattori on funktio, joka saa operaattorista riippuvat parametrit. Se siis toimii kuin Lisays(Tyontekija&) -funktio toimisi aikaisemmassa esimerkiss�. Operaattorifunktiota voidaan kuitenkin kutsua niin kuin operaatoreita k�ytet��n. Funktio m��ritell��n operator-sanalla. Sen per��n kirjoitetaan haluttu operaattori, siis vaikka operator+. Jotta operaattorit ++olio ja olio++ voitaisiin erottaa toisistaan, m��ritell��n j�lkimm�isempi (postfix-muoto)
int operator(int) {}
Siis se saa int-tyyppisen haamuparametrin, parametrin joka ei sis�ll� mit��n. T�ss�p� sit� on sitten ylikuormitusta kullekin.
#include <iostream.h> class Tyontekija { public: void AsetaPalkka(int p) { palkka = p; } int AnnaPalkka() { return palkka; } int operator+(Tyontekija& toinen) { return palkka + toinen.AnnaPalkka(); } private: int palkka; }; int main() { Tyontekija jamppa; jamppa.AsetaPalkka(8000); Tyontekija jomppa; jomppa.AsetaPalkka(12000); cout << "Kaveruksille pit�� maksaa yhteens� " << jomppa + jamppa << endl; return EXIT_SUCCESS; }
Nyt voimme kirjoittaa kryptatusta taulukosta hienon luokan. Sit� voi k�ytt�� kuin taulukkoa, mutta se on kuitenkin luokka - siis sijoituksien oikeellisuus voidaan tarkistaa, muutamia hienouksia mainitakseni.
#includeconst int OLETUS_KOKO = 256; class KryptattuTaulukko { public: KryptattuTaulukko(int koko = OLETUS_KOKO); // oletusparametri ~KryptattuTaulukko(); int Alusta(); // todellinen muodostinfunktio void Koodaa(); int AnnaKoko() { return taulukonKoko; } int OnkoKoodattu() { return onKoodattu; } int OnkoAlustettu() { return onAlustettu; } int& operator[](unsigned kohta); // voidaan sijoittaa kuin taulukkoon int operator[](unsigned kohta) const; // voidaan lukea kuin taulukkoa void operator=(const int* arvot);// voidaan sijoittaa taulukollinen arvoja private: int onAlustettu; int onKoodattu; int *taulukko; int taulukonKoko; int virhe; // virheelliset viittaukset ohjataan t�nne }; KryptattuTaulukko::KryptattuTaulukko(int koko) : onAlustettu(0), onKoodattu(0), taulukonKoko(koko), virhe(0) { // tyhj� muodostimen runko } int KryptattuTaulukko::Alusta() { taulukko = new int[taulukonKoko]; if (taulukko == 0) return 0; onAlustettu = 1; return 1; } KryptattuTaulukko::~KryptattuTaulukko() { if (onAlustettu) delete [] taulukko; } int& KryptattuTaulukko::operator[](unsigned kohta) { if (onAlustettu) { if (kohta < taulukonKoko) return taulukko[kohta]; // laillinen sijoitus else return virhe; // ylivuoto } else return virhe; // vakion nolla palauttaminen ei olisi mahdollista, koska se ei ole olemassa kuin metodissa } int KryptattuTaulukko::operator[](unsigned kohta) const // unsigned est�� laittomat negatiiviset indeksit { if (onAlustettu) { if (kohta < taulukonKoko) return taulukko[kohta]; else return 0; } else return 0; // ei tarvita viitattavaa kohdetta, joten nolla k�y } void KryptattuTaulukko::operator=(const int* arvot) { // vaarallista, const int* arvot eiv�t v�ltt�m�tt� osoita taulukonKoko kokoiseen muistialueeseen... if (onAlustettu) for (int a = 0; a < taulukonKoko; a++) taulukko[a] = arvot[a]; } void KryptattuTaulukko::Koodaa() { for (int i=0; i < taulukonKoko; i++) taulukko[i] = 255 ^ taulukko[i]; // py�r�ytet��n bitit ymp�ri if (onKoodattu) onKoodattu = 0; // vaihdetaan koodauksen tila else onKoodattu = 1; } int main() { int alkupTaulu[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; KryptattuTaulukko taulu(10); taulu[5] = 5; // ei tee mit��n, koska ei alustettu if (!taulu.Alusta()) return EXIT_FAILURE; // taulukon luonti ep�onnistui taulu = alkupTaulu; // kopioidaan alkupTaulu taulu:n taulu[-1] = 5; // kumpikin laittomia sijoituksia, eiv�t tee mit��n taulu[100] = 5; taulu.Koodaa(); cout << "Koodattu taulu: "; for (int a = 0; a < 10; a++) cout << taulu[a] << " "; taulu.Koodaa(); cout << endl << "Koodaamaton taulu: "; for (int b = 0; b < 10; b++) cout << taulu[b] << " "; cout << endl << endl; cout << "Ensimm�inen alkio on nyt " << taulu[0] << endl; taulu[0] = 7; cout << "Sijoituksen j�lkeen se on " << taulu[0] << endl << endl; cout << "Tilamuuttujat: koodaus:" <
coutin todellinen luonne
Itse olen joskus kummastellut: "C++:ss� on olemassa muuttujia, funktioita, luokkia ja olioita. Mik� ihmeen vetkutin se cout sitten on?" Vaikka ihan ensimm�isest� ohjelmasta asti olet k�ytt�nyt cini� ja coutia, niin silti niiden olemus lienee hieman ep�selv�.
cin ja cout ovat olioita. Ne ovat iostream.h:ssa (tai sen alaisessa otsikkotiedostossa) valmiiksi m��riteltyj� olioita. Niist� l�ytyy uudelleenm��riteltyn� << tai >> operaattori. Siksi siis kun k�skemme coutia tulostamaan - siis kutsumme sen j�senfunktiota - ei toimenpide vaikuta milt��n funktiokutsulta, vaikka se sit� onkin. Sellaisia peijooneita ne operaattorit ovat, varsinkin uudelleenm��ritellyt. << -operaattorin k�ytt�minen kutsuu todellisuudessa operator<<(...) -funktiota, jonka me alla olevassa esimerkiss� esiinmerkitsemme:
#include <iostream.h> int main() { int luku = 5; cout << "Ik�ni on " << luku << endl; cout.operator<<("Ik�ni on ").operator<<(luku).operator<<(endl); return EXIT_SUCCESS; }Esimerkin kahden tulostuslauseen tulostama teksti on sama, k�yt�mme funktiokutsun mit� muotoa tahansa. Monien per�kk�isten funktiokutsujen ketjuttaminen onnistuu sen vuoksi, ett� operator<<(...) palauttaa viittauksen coutiin. Siis alku:
cout.operator<<("Ik�ni on ")... muuttuu funktiokutsun j�lkeen lausekkeeksi...
cout.. ja sen per��n voi lyk�t� uutta funktiokutsua ihan rauhassa. Kuten ehk� arvasitkin, n�m� pohdinnot ovat kuin hyv� ranskanleip�: ne toimivat my�s toisesta p��st� pureskeltuna - siis sama idea p�tee ciniin, operaattorin nuolet ovat vain toiseen suuntaan.
Osoitin j�senfunktioon
Piti sitten t�llekin asialle oma kappale kyh�t�. No, nyt on komia paragraafi, niin pit�� sitten jotakin yritt�� t�h�n rustata.
void (Luokka::*pFunktio)(int, char);
Voi kehveli, tuohon se nyt lipsahti. Mahtaako sit� en�� mit��n sanoa? Siis * merkkaamaan osoitinta, luokka ja n�kyvyysoperaattori (siis :: ) alkuun. Ja asteriski eli * ennen funktion nime�, ei koko rimpsun alkuun, koska luokalla ei olla osoittelemassa mihink��n.