Kaukonäköispatsas eli C++ tutti numero 2 ======================================== OSA II Maailmanepämaineeseen nousseen ensimmäisen C++ -tutoriaalin toinen osa, jossa jatketaan perehtymisentapaista opiskeluprosessia, jonka tarkoitus on oppia C++ -kieltä. Tai siis virallisemmanpuoleisesti näin. Oikeasti tässä puhutaan hassuja, juodaan colaa ja pidetään silmälaseja väärinpäin. Silmälasittomat (kuten minä) voivat pitää silmiään väärinpäin. Ja jolleivat he siihen kykene (kuten minä en kykene), niin pitäkööt sitten T-paitaa väärinpäin. Kaikillahan on T-paita, eikös juu? No jollei ole niin teemme T-paidan itse sitten. Joten seuraa valmistusohje: lämmitä vettä esimerkiksi mikroaaltouunissa tai kattilassa. Etsi teepussi ja liota sitä tuossa lämmitetyssä vedessä 3-5 minutia. Kun olet valmis, ota käteesi ihan mikä tahansa paita ja kaada tee sen päälle. Nyt laita paita väärinpäin päällesi. Olet valmis aloittamaan! Disclaimer ---------- Tässäpä ei ole disclaimeria tai mitään muuta varoitusta, koska sellaiset on todettu täysin turhiksi. Tai no ei oikeasti ole, joten kaipa tähän pitää pieni sitten kirjata. Enpäs siis ota vastuuta esimerkkien toimivuudesta tai asioiden oikeellisuudesta. En minä näitä esimerkkejä mitään ole testannut, kunhan kirjoittelen tänne ja toivon, että toimivat eivätkä sisällä kirjoitusvirheitä tahi muuta epäloogista. Niinjoo, kun kaikki muutkin opetusmateriaalien ja kirjojen kirjoittajat kehuvat niinpaljon itseään, niin pitäähän minunkin sitten varmaan. Elikkä C++ -ohjelmointia olen harrastanut jo ainakin muutaman kuukauden. Tosin nyt kun alan miettimään, niin kyllähän siitä on pidempi aika. Paitsi jos muutama kuukausi on yli kymmenen tai vastaavaa. Tosin en tuostakaan ole varma. Tuosta kymmenestä voin ehkä mennä melkein varmasti sanomaan, mutta enempää en lupaa kun sormet loppuu kesken enkä muuten osaa pitemmälle laskea. Niin ja on minut oikeen koulutettukin! Kun piti mokoma C++ -kurssi käydä opiskeluiden takia niin semmosen sitten kävin. Tai no ei mikään pakko ois ollu, mutta kävinpä kuitenkin. Tais olla kolmen opintoviikon arvoinen se. Ja läpi pääsin! Että emmää mikään ihan turha jätkä ole! No lähellä, mutta en ihan. Eiks juu? Niinjoo, ja näitä kompuutteriapparaattejakin olen muutaman vuoden jo ehtiny käytellä. Että osaan painella oikeita nappeja enkä kirjota esimerkiksi "hdakdhjjg" kun pitäis "tervehdys" kirjoittaa. Ja tässäkin tuo muutama on ainakin muutama. Voisin melkein lupailla, että se ois ehkä yli kymmenen, mutta en voi taaskaan mennä takuuseen kun ei ole kymmentä enepää sormia. Vois tän kompuutteriaparaatin laittaa laskemaan, kun sillä on kuuleman mukaaan ainakin kuustoista sormea. On nääs lisäksi tämmöset A, B, C, D, E ja F -sormet. Niillähän se laskeepi tuon ilman ongelmia kun tuskin minoon yli kuuttatoista vuotta näijen kanssa säätäny. Vai oonkohan minä? No hetkinen kun minä koodailen hetkisen. Odottakkees... Noni, toivottavasti ei kestänyt liian kauan, kun piti kaikkea hassua tuossa samalla tehdä. Niinjoo, sitähän minä olin sanomassa, että sain tuon ohjelman laskemaan, notta kauanko oon näiden aparaattien kanssa säätäny. Ja tuo mokoma kehtaisi väittää, että nelejätoista vuotta ois mokomaa tehty. Uskoakko ken haluaapi, tai jos ei Ken halua niin kysyppä Parpilta. Se vois halutakkii ja kyllä se Parpi ois parempi kuin Ken kun sentäs... Niinjoo. Eiköhän tämä tällä kertaa riitä. Ja kaikille tiedoksi vielä, että tätä saa levitellä vapaammanpuoleisesti, aivan kuten naapurin teepeet saa levitellä omia antejaan, kunhan ei ihan julkeasti mene levittelemään. Tai no kyllä tätä saa levittää kun ei sitä ole toistaiseksi laissakaan kielletty. Tai no on, sillä tämähän on teos ja tekijänoikeuden alainen, mutta kun näistä teoksista saa määrätä se tekijänoikeuden haltija, joka sattuu olemaan minä, niin minäpä päätänkin, että tätä saa levitellä. Ei kyllä menis läpi tämä selitys siinä naapurin teepeen tapauksessa, vaan vankilaan pistäsivät tai ainakin sakkoja antasivat. On kyllä ihme touhua. Mutta enpä voi mitään, niin on joku minua viisahampi mennyt päättämään. Tosiaankin semmoinen lisäys vielä, että se paritus on kieällettyä niin eipä mennä sutenööriperkeleet perimään tämän levityksestä maksua elikkäs ilmahteeksi pitääpi siirrellä muille immeisille. Hei, maailma! ------------- Mikä olisi tutoriaali ilman maailman tervehtimisesimerkkiä? No se olisi tietenkin tutoriaali ilman maailman tervehtimisesimerkkiä. Tämä ei kuitenkaan ole sellainen tutoriaali vaan ihan tavallinen, tuttu ja turvallinen, maailman tervehtimistutoriaali. Joten tässä sellainen: ---[ hello.cpp ]--- #include int main () { std::cout << "Hei, maailma!" << std::endl; return 0; } ------------------- Ohjelman voi kääntää esim. komennolla: g++ -o hello hello.cpp -W -Wall Nuo -W -Wall on sen takia, että... Hmm. Tämä kuulostaa jotenkin tutummanpuoleiselta. Ootappa kun katselen hieman... Joo, täähän käsiteltiin jo siinä ekassa osiossa, hyvä että kirjotin tän oikein lapulle asti. Niin, eli mennään sitten enemmittä mukisematta seuraavaan aiheeseen. Niin, se seuraava aihe vois olla joku C++ -kieleen liittyvä, etten ala esimerkiksi Epsanjan kieälen alkehia opettamaan. Tuosta tulikin mieleen, että miksei mikään maa ole ottanut C-kieltä tahi tätä C++ -kieltänsä viralliseksi kielekseen? Vois esimerkiksi Uusi-Clanti ottaa tommosen kielekseen. Siellä sitten ois mukavaa kaikkien tätä tuttia lukevien puhua. Kylläpä joo. Niin se uusi kappale. Ylilataus --------- Siis huonon sisäisen lukutaidon ihmisille tuossa lukeepi YLIlataus ei YDINlataus, kuten joku voisi luulla. Ja tässä ei sitten ole mikään akku ylilatautunut tahi muu vastaava vaan ihan C++ operaattorit. Semmosia operaattoreita on katsos tuossa alla olevassa listassa olevat vermehet: + - * / = < > += -= *= /= << >> <<= >>= == != <= >= ++ -- % & ^ ! | ~ &= ^= |= && || %= [] () new delete Nythän voi joku oikein fiksu ja filmaattinen henkilö keksiä, että ainaskin operaattorit << ja >> on yliladattu aiemmin tuossa cout:n yhteydessä. Jaa ja sen, mitenkäs noita sitten lataillaan ja yliajetaan, kerron nyt jollei joku tule minua estämään. Tuosta tulikin mieleen eräs baari, jossa bailasin avaruusolion kanssa aamuneljään. Kunhan muistaisin vain sen paikan nimen... No kerron sitten, kun muistan. ---[ ylilataa.cpp ]--- #include #include using namespace std; class Koordinaatti { private: int x,y; public: Koordinaatti() {}; Koordinaatti(int a, int b); Koordinaatti operator+ (Koordinaatti p); string tulosta(); }; Koordinaatti::Koordinaatti(int a, int b) { x=a; y=b; } Koordinaatti Koordinaatti::operator+ (Koordinaatti p) { Koordinaatti tmp; tmp.x=x+p.x; tmp.y=y+p.y; return tmp; } string Koordinaatti::tulosta() { ostringstream tmp; tmp << "(" << x << "," << y << ")"; return tmp.str(); } int main() { Koordinaatti a(4,6); Koordinaatti b(3,2); Koordinaatti c; c=a+b; cout << a.tulosta() << endl; cout << b.tulosta() << endl; cout << c.tulosta() << endl; return 0; } ---------------------- Elikkäs noinikään tuo toimii. Kaikki muut operaattorit voidaan yliladata samaan tapaan, jolloista joo. Nyt minä teen ruokaa. Mussutelkaa te tätä tuttia sillä aikaa. Ei siitä nälkä lähde, mutta onhan se mukavaa touhua, eikö vain? Joo, I'll be back elikkäs minusta tulee joskus selkä... Noin, nyt kun olen ruokailun äärestä selkäistynyt, niin voidaan jatkaa etiäppäin. Myös funktioitakin voidaan yliladata, elikkäs tarkoittapi sitä, että samanniminen funktio voidaan määritellä useampaan kertaan. Eri funtkiot erotellaan parametrien tyyppien ja määrän mukaan. Erkki se jaksaa merkkejään jaella. Siis esi-semmosia: ---[ ylilataa2.cpp ]--- #include int laske(int a, int b) { return a+b; } int laske(int a) { return a+a; } double laske(double a, double b) { return a+b; } double laske(double a, int b) { return a+b; } int main() { std::cout << laske(1,2) << std::endl; std::cout << laske(2) << std::endl; std::cout << laske(2.3,1.2) << std::endl; std::cout << laske(2.3,2) << std::endl; return 0; } ----------------------- Näinikään, joo. Sitten ois varmaan aika alkaa vesileluja etsimään kaapista. Tai missä ikinään niitä säilytättekään. Tarkoitus olisi mennä uimaan, pulipuli, tai jotain vastaavaa. Varamaan jotain vastaavaa sillä joo. Mutta ainakin loogisuus on säilynyt. Siis tässä epäloogisuudessa. Eli asiat on käsitelty loogisesti epäloogisesti. Seuraavassa siis tulevaisuuden uudelleenpainosvihjeitä. No emmäjaksa, kun ei kuitenkaan uusintapainosta koskaan julkaista. Ehkä sen takia, kun tätä ei koskaan varmaan tulla edes painoksena julkaisemaan. Oletetusti myös tämä lukijakunta rajoittuu ehkäpä varovasti arveltuna kolmeen henkilöön. Tai no, ollaan hieman optimisisempia, ja arvataan vaikkapa viittä lukijaa. Ei samoihin lukijamääriin kyllä mikä tahansa tutoriaali yllä! Eheehei! Mutta nyt kaikki bussihin ja bussin keula kohti etelän rantaisia. Semmonen nakuranta oikein, missä sais kulkia kulli tuulessa... Tosin voisi olla sitten noloisemmanpuoleista, jos sitten kaunihimman sukuisen puolen edustajistoa liialti näkyilisi ja herran kurkkukeppi sitten alkaisi viisaroimaan kohti keskipäivää. Tosin eipä siinä mitään jos nämä vastapuolen edustajat sitten suuntaisivat karvalapasensa kohti liftarin peukkua. Mutta mikä on elämässä unelmaa ja totta... Virrat ------ Varrelle virran suunnistakaamme nyh. Rantalelut, uimahousut, pyyhe, aurinkovoide, pelastusliivit ja C++ -tutti mukaan. Hieman aurinkovoidetta selkään ja reiden sisäpintaa hyväilen, ja... jatkan tutin kirjoittamista. Mutta kuten jotkut tarkkakorvaiset pulloposket ovat huomanneet, virroissa olemme liikkuneet ainakin tuon iostream:n ja sstream:n kanssa. Eli ensimmäinen on syöttö- ja tulostusvirrat ja jälkimmäinen merkkijonovirtoja (string stream). Virtoihin kirjoitetaan operaattorilla << ja luetaan operaattorilla >>. Alunperin C:ssä näiden tarkoitus on ollut bitittäinen siirto vasemmalle sekä oikealle, mutta C++:n mahdollistaman ylilataamisen vuoksi niille on keksitty uutta käyttöä. Toki niitä voi vielä oikeanpuoleiseen tarkoitukseensa käyttää, ja niin toimivattenkin nuo oletuksena, jollei ylilautausta ole harrastettu. Ennestäisestään C++:ssa on määritelty edellä mainittujen lisäksi muitakin virtoja. Oikeastaan virrat on hierarkinen rakenne, jossa on periytetty toinen luokka toisesta ja kolmas toisesta ja neljäs... Piirrän hieman (selitänpä sitä ennen kuitenkin, eli ja sen alla luokat ja periytysnuolet, joissa nuolen osoittama periytetty ja nuolen alkupäässä periytettävä): ios_base /-> istream ---\--> cin | / | \-----------> ifstream V / V \----------------------> istringstream ios ---| iostream -------------\-> fstream \ ^ \------------> stringstream \--> ostream -\----------\---> ofstream \---> cout \--------------> ostringstream \--> cerr \-> clog Tuostahan saa jopa selvää! Melkein. Ainoa selitettävä kohta lienee tuo ostream, eli muutoin nuo otsikkotiedostojen nimet on ylhäällä ja sen alla luokat, mutta tuossa ostream-otsake tuo tuon ostream-luokan (ei muuta) ja se istream istreamin ja iostreamin. Tuolla vilisee nyt merkkejä joka lähtöön, mutta i=input, o=output ja f=file. Ja koska ovatten kaikki samaa perua, niin samoja metodeja siellä vilisee ja esiintyypi. En ala tässä nyt semmosia listailemaan tahi erittelemään vaan niitä voipi esimerkiksi nettilöisestä etsiskellä tai jostakin. Nonii, hopihopi, seuraava aiheistus. Pölyn muuntautumiskyky ---------------------- No oikeasti puhutaan polymorphismista elikkäs monimuotoisuudesta. Tämä tarkoittaapi sitä, että olkoon meillä vaikka luokka X, josta periytetään luokat Y ja Z. Nyt nämä kaksi periytettyä luokaa ovat periaattessa luokkia X. Voidaan siis tehdä (oletetaan, että luokilla on virtuaalinen metodi nimi(), joka tulostaa luokan nimen eli X, Y tai Z): int main() { //Luodaan luokista Y ja Z esiintymät Y luokkay; Z luokkaz; //Luodaan osoitin luokkaan X X* luokkax; //Asetetaan luokkax-osoitin osoittamaan luokkay:hyn. luokkax = &luokkay; luokkax->nimi(); //Asetetaan luokkax-osoitin osoittamaan luokkaz:än. luokkax = &luokkaz; luokkax->nimi(); //Luodaan uusi luokan X -ilmentymä ja tallennetaan osoitin luokkax:ään luokkax = new luokkax(); luokkax->nimi(); } Tulostus olisi: Y Z X Tässä tuli muuten samassa, tai oikeastaan tätä on jo käytetty useasti, esille dynaaminen sidonta (dynamic binding). Se tarkoittaa sitä, että metodikutsun osoite määritetään vasta ajon aikana. Edellisessä esimerkissä on kolme luokkax->nimi() -kutsua. Käännöksen aikana ei voida tietää, minne tuo oikeasti viittelöi vaan kohde selvitetään ajon aikana. Yksi esimerkki edellisiä luokkia käyttäen: int main() { Y luokkay; Z luokkaz; X* luokkax; if (rand()%2==0) luokkax=&luokkay; else luokkax=&luokkaz; luokkax->nimi(); return 0; } Kyseenomaisessa esimerkissä ei voida mitenkään tietää, minkä luokan nimi() -metodia tuossa kutsutaan. Wöndevyl! Sabluuna -------- Ja sakset sanoi, niks ja naks ja pöks. Ja näin saatiin kissahalle vaattehet valmiiksi, kun oli sopivat sabluunat. Kas kummaa, samoja sabluunoita voitiin käyttää uudelleen hiiren vaattehille! Elikkäs nyt perehtykäämme sabluunoihin (templates). ---[ sabluuna.cpp ]--- #include using namespace std; template void vaihda(T &a, T &b) { T tmp; tmp=a; a=b; b=tmp; } int main() { int a=3, b=4; double c=5.4, d=3.3; cout << "Ennen : " << a << " ja " << b << endl; vaihda(a,b); cout << "Jälkeen: " << a << " ja " << b << endl; cout << "Ennen : " << c << " ja " << d << endl; vaihda(c,d); cout << "Jälkeen: " << c << " ja " << d << endl; } ---------------------- Eli aluksi esittellään metodi ja sabluunoitava luokka. Tätä luokan tunnusta sitten käytetään kuten mitä tahansa tyyppiä. Kun tuota funktiota sitten käytellään, pitää tuo käytettävä tyyppi määrittää <> -merkkien välissä, jolloin tuossa funktiossa kaikki nuo tunnukset korvataan ko. tyypillä. Toimii ihan mille tahansa tyypille, tuossa voisi laittaa vaikka luokan, tms. tilalle. Toinen esimerkkilöisyys: ---[ sabluuna2.cpp ]--- #include using namespace std; template A suurempi(A x, B y) { return x>y?x:y; } int main() { int a=4; long b=5; double c=4.2; cout << suurempi(a,b) << endl; //Seuraava rivi aiheuttaa kääntäjän varoituksia, niistä ei tässä //tarvitse välittää, mutta oikeassa maailmassa ihan hyvä tietää, että //nyt tehdään jotain ei niin kovin laillista eli tuossa joudutaan //muuntamaan double-tyyppinen muuttuja int-tyyppiseksi ilman //tyyppimuunnosta ja siitähän voi seurata vaikka mitä hassua, //mutta tässä tapauksessa pitäisi tulokseksi tulla sama kuin (int)c //eli jotakuinkin 4 (eli desimaaliosa unhoitetaan). cout << suurempi(a,c) << endl; cout << suurempi(c,a) << endl; cout << suurempi(c,b) << endl; return 0; } ----------------------- Joo näinikään eli joo. Kätevätäpätevätä, joopa. Nyt sitten vaan kissahan vaattehia hiirelle sovittamaan - ja päinvastoin. Hus siitä! Hajaantukaa, tämä tutoriaali alkaa olemaan lopussansa eikä tässä enää mitään asiaa ole. Poishpoihs! Nonii. Nyt on hiljaista joten voin aloittaa loppupuheenvuoroni eli epilogia sinne tahi tänne näin ja päin. Loppusanat ---------- Jaa vielä lisää vai? No pakkohan sitä on kun vaaditta. Niin siis joskus. Elikkäs nyt minä huomaan puhuneeni aivan liikaa, ja toivottavasti nyt ei joudu sitten keksimään mahdollisiin myöhempiin osiin enempää paskaa tai no mitä väliä sillä on, kivahan tätä on jauhaa, tai no en tiedä nyt lukijoistapukijoista, muttajoo. Niin. Elikkäs nyt ois jo jonkun vertaisen tätä C++ -asiaa käyty, tosin melko sekavassa järjestyksessä ja huonosti selittäen, joten kyllä jossain vaiheessa on lisäosille tarvetta, varsinkin kun olen tahallani ainaskin yhden asian jättänyt selittämättä. Niin ja jos tarvehetta on, niin kai sitä pitää mennä vähän C-kielen jalkojen päällekin astumaan ja kertoa hieman näistä näiden yhteheisistä piirteistä ja eroista. Niin ja semmottinen sekoiluosa ois varmaan mukava, kun eräistä lukijoista tämä muka on liian selväjärkistä puheensolinaista, notta paskaa saisi jauheskella kuin lehmä heinää keskellä kesää. Niin, jotta ei tarvitse lukijoita jännityksessä pitää niin kerronpa, että ainakin poikkeukset on käsittelemättä (hih) niin ja näemmä myös tyypinmuunnokset. Kaikkea hassua sitä tuolla tyduu- listalla on. Ja lupasinko minä mokoma vielä esikäsittelijän ohjauskäskylöisiä? No luvataan nyt niitäkin. Ja riippuen siitä, mistä tuo on roikkuen kiinni katossa, eli paljonko täällä on kysynnäntapaista, niin varmaan selittelen vielä tuon melkein kasaan suunnitellun neljännen osan, jollen nyt innostu niistä asioista seuraavassa osassa puhelemaan. Mutta nyt keksittynen johonkin jonninjoutavaan. Kuitenkin joskus kyllästyn miettimään syntejä syviä ja matalia ja päätän aikani tuhlata jonninjoutavuuden sijasta vähemmän jonninjoutavaan tutoriaalin kirjoitteluun. Ja sen takia tämä ei jonninjoutavaa ole, kun minä sanon niin. Kyllä tämä varmasti lämmittää esimerkiksi monen afrikkalaislapsen sydäntä, ja lähettelevät taas minulle noita kirjehiä jossa lupaavat miljooniansa, kunhan autan niitä siirtämään vaan niitä jonnekin kun on niillä niin kauhian huonot oltavat ja paha sotilasjuntta on juuri ottanut vallan ja tietenkinhän nämä rikkaat on heti tykinruokana tai ainakin rahat on ihan hetikohta joutumassa pahan haltuun. Siksi nämä epätoivoiset ovat tämän tutoriaalin luettuaan päättäneet antaa minun auttaa. Ja kunhan autan, niin saan sen kymmenen prosenttia tuosta rahaisesta määrästä. Ja sen kymmenen prosenttia sen takia kun ei nekään osaa enempää laskea, kun loppuu sormet kesken. Tosin nillä voi loppua aiemminkin, jos niitä on vaikkapa kidutettua ja niiltä puuttuupi sormia. Mutta olen aina tarkistanu että niillä on tarvittava määrä sormia kädessänsä, etten minä joudu tyytymään pienempään määrään. Ja oikein huijareitakin ovat joskus olleet. Eli ovat menneet itse puremaan sormiansa poikki. Siinä säästävät sitten pitkän pennin mokomat. Mutta niin, loppu joo. Heihei! Tämän tutin avulla jopa sokeat oppivat näkemään hyvin! (Even blind people can learn C++ with this tutorial). Meremoth-2. Hetken mietin tuttia, tuttia ja huttia. Eikä pidä unohtaa myöskään luttia, jonka olemassaolevuus unohtuu usein ja monesti, silloin ja tällöinsä. Tosin nyt kyllä aiheesta eksyimmä, tutti olikin tutoriaali tapauksessa tässä, tuon toisen tarkoitusta on viisaat päät miettimässä. Copyright (c) kakstuhatta ja kolome vuotta ajanlaskumme jälkeen, Joro (Ja minä olen edelleen tuo Joro, elikkäs Jouni Roivas oikeanpuoleiselta nimeltäni, ja http://roivas.org on kotoisan sivun osoite, sähköistä postia saa edelleen lähetellä osoitteeseen jroivas@iki.fi. Pitäisi muuten tarkistaa tuon toimivuus kun ei sinne tule kuin roskapostia. Nii semmosta roskapostia, että pitää oikein suodattimet olla. Onneksi nämä suodattimet ei mene tukkoon niinkuin suodattimilla on yleensä tapana vaan tämä osaa ihan ite ittensä pitää puhtaana joo.)