Kaukonäkö eli C++ ================= OSA I Tarkoitus olisi opettaa C++ -ohjelmointikieltä. Oletan, että lukija osaa ohjelmoida jollain muulla ohjelmointikielellä, esimerkiksi C-kielellä, tai on ollut jopa tekemisissä C++ -kielen kanssa aiemmin. Pohjatietona voi olla myös esimerkiksi Perl-, PHP-, Java- tai Pascal-kielen tuntemus, vaikka en selvitä ihan perusasioita, pitäsi näillä ohjelmoineen ymmärtää kielen rakenne ja syntaksi esimerkkejä tutkimalla. Eipäs kuitenkaan turista ja porista liikaa näin heti alkuun. Vasta seuraavassa... eiku sovitaan, että kolmannessa kappaleessa vaikka. Joten innokkaasti tätä tuttia eli tutoriaalia lukemaan! 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. Saatanpa vaan olla joku hörhö, joka on viinaspäissään keksinyt puhua jostain ihmeen C ja C++ -kielistä. Miksei samalla ABC-kielestä? Tai vaikka samantien voisin puhua jostain oletetusta ameriikanmaan presidentistä, jonka älykkyysosamäärä ois 80 eli vain 5 yksikköä enempi kuin Forrest Gumpilla, mutta onneksi ameriikan kansa sentäs on sen verta viisahia (eli keskijakauman mukaan 20 yksikköä viisahampia kuin tuo oletettu presidentti), etteivä ikumaailmassa tuollaista menis äänestämään johtoon. Sehän voisi vaikka päättää, että ovat jotain herraskansaa, kuten se Hitlerikin päätti. Ja sittehän ne ois ihan hiphurraa kaikki. Päättäsivät, että mehän vallataan tosta joku öljymaa, vaikkapa Irakki ja sittenpähän niillä ois öljyä elää herroina ja käydä sotiansa. Tosin kyllähän joku varmaan ois järkevä, ettei tuollaista sallisi. Paitsi jos keksisivät jonkun hyvän syyn. Vaikkapa räjäyttäävät Valkosen talon. Tai no ei sitä, kun se on liian kallis paikka. No jonkun purkutuomion saaneen pilvenpiirtäjän vaikka sitten. Tai no laitetaan kaksi samaan hintaan. Sitten sanotaan, että terroristit sen teki juu, ja Irakissa majaileevat. Tai jossain lähistöllä, kato kun saisvat sitten hyvän tukikohdan sinne ja seuraavaksi vasta Irakiin joo. Mutta onneksi ei tarvitse siitä puhella, vaan puhellaan vaikkapa tästä C++ kielestä joo. Mutta kaipa se nyt sitten tuli selväksi, että lukijahan tässä lukee ja kokeilee noita esimerkkejä omalla vastuullansa. Eiköstä juu? Sitä piti vielä sanomani, että tätä saa levitellä vapaammanpuoleisesti, aivan kuten naapurin leskirouva 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 rouvan 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. Johdanto -------- C++ on C-kielestä kehitetty versio, johon on lisätty olio-ohjelmointipiirteitä (ja tiedoksi, notta C++ EI ole olio-ohjelmointikieli, kuten usein kuulee väitettävän). Uusimmassa C++ -standardissa (kauhean hankala sana, miksi ihmeessä minä sen kirjoitan, vaikka normi olisi helpompi ja Suomea ja ties mitä. Ja kun muualla käytän suomenkielisiä termejä, niin miksen tässä? Hassu minä. Eikun sinä, eikun en minä enää tiedä. Jatka lukemista kuin mitään ei olisi tapahtunut...) on eräitä muutoksia verrattuna vanhaan standardiin. Uuden standardin pitäisi olla taaksepäin yhteensopiva, eli ohjelmien pitäisi kääntyä vanhalla kääntäjällä (mutta ei välttämättä toisinpäin, sillä ne örvelöt tahtoo, että kirjoitetaan tämän uudemman ja paremman mukaan, joka on ihan hyvä asia, kun tämä uudempi on sentään uudempi ja parempi). Suositeltava kääntäjä on GNU C-kääntäjän versio 3.0 tai uudempi ja kääntely olisi hyvä suorittaa Linux-ympäristössä. Toki muut *NIX-järjestelmät kelpaa, kunhan niihin saa GNU-projektin kääntäjän. Ja ei sitä GNU-kääntäjääkään välttämättä tarvi, kun standardia kirjoitellaan, mutta kun monet muut kääntäjät ei välttämättä oikein tämän päälle ymmärrä, kun ovat kehitetty esiliitukaudella tai jossain, jolloin ei tästä uudesta oltu kuultu tai sitten ne eivät ole halunneet tuota uutta alkaa noudataamaan ennen seuraavaa uutta versiota, joka taas tulee sitten joskus. Ai niin... Eli pidemmittä höpisijöittä edemmäs eli tuonne alaspäin eli aloitetaan! 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 ]--- /* Tämä on kommentti, tämä lasketaan valkoavaruudeksi eli näkymättömäksi. Valkoavaruutta (whitespace) on myös avaruus (space eli välilyönti), tabulaattori (eli askelpalautin, ei siis mikään tabu, tästä saa kyllä puhua ilman pelkoa muiden halveksinnasta). Kommentin alkamismerkki on kauttaviiva ja tähti. Lopetusmerkki on tähti ja kauttaviiva. Samanlainen kommenttikäytäntö on myös C-kielessä. Kommentit on tarkoitettu lähinnä koodin lukijalle (muille soodareille tahi koodaajalle itselleen selvittämään, mitä koodissa tapahtuu. */ //Tämä on myös kommentti. Kommentti alkaa kahdella kauttaviivalla ja päättyy //rivin loppuun. Tätä kommentoimistyyliä ei ole määritetty C-kielessä. //Siis siinä standardinmallisessa, joissain laajennoksissa kylläkin, mutta //tämänhän piti olla C++ -tutoriaali, joten palataanpa takaisin tähän //aiheeseen. //Otetaan mukaan iostream-otsikkotiedosto //Siinä on olevinaan I/O (Input/output) ja virtaa. Eli C++ käyttää erilaisia //virtoja, varsinkin luku- ja kirjoitustoiminnoissa (tuo mystinen I/O). #include /* main -metodia kutsutaan ohjelmaa käynnistettäessä. Sille annetaan parmetrina komentoriviparametrien määrä ja osoitin taulukkoon, jossa on osoittimet kyseisiin parametreihin. */ int main (int argc, char** argv) { // Viitataan muuttujiin itseensä, jolloin niitä on käytetty eikä // kääntäjä valita (void) argc; (void) argv; // std::cout on tulostusvirta, joka yleensä vastaa STDOUT:a. // std::endl on rivinvaihto, joka vastaa merkkiä '\n', lisäksi puskuri // tyhjennetään eli data kirjoitetaan oikeasti kohteeseen std::cout << "Hei, maailma!" << std::endl; // Palataan funktiosta ja palautetaan paluuarvo 0 (ei virhettä) return 0; } ------------------- Ohjelman voi kääntää esim. komennolla: g++ -o hello hello.cpp -W -Wall Nuo -W -Wall on sen takia, että nähdään kaikki mahdolliset virheet. Näiden koodiesimerkkien ei pitäisi tuottaa virheitä tai varoituksia, joten jos sellaisia saat, niin varmaan olisi syytä tarkistaa syntaksi. Jos se on varmistettu niin kannattaa varmaan varmistaa, että se kääntäjä tajuaa oikeasti jotain eikä ole joku ihmeen vajaakääntäjä, joka ei osaa edes kahteen laskea (kun eivät ole laskimia) tai varmista, että oikeasti on joku ohjelma olemassa mitä käännetään, kun eihän tyhjää voida kääntää, tai no voidaan, mutta siitä tulee käännettyä tyhjää eli tuli varmaan selväksi. Jos ei tullut niin kirjoitan sitten joskus myöhemmin tyhjentävän selityksen. Nimiavaruus ----------- Tuossa edellisen kappaleen esimerkissä (aasi se siltaa tepsutteli) viittelöitiin kummallisesti noita cout ja endl -vermehiä. Eli edessä oli nimiavaruuden (namespace) tunnus. Nimiavaruus on alue, johon voidaan määrittää nimiötä siten, että niiden näkyvyys on vain nimiavaruuden sisällä. Esimerkki: ---[ namespace.cpp ]--- #include //Määritellään nimiavaruus "yksi" namespace yksi { //Kaksi kokonaislukumuuttujaa int a; int b; //Funktio laskee muuttujien arvojen summan int laske() { return a+b; } } //Määritellään nimiavaruus "kaksi" namespace kaksi { //Kaksi liukulukumuuttujaa double a; double b; //Funktio laskee muuttujien arvojen summan double laske() { return a+b; } } int main (int argc, char** argv) { (void) argc; (void) argv; yksi::a=2; yksi::b=3; std::cout << yksi::laske() << std::endl; kaksi::a=2.3; kaksi::b=3.4; std::cout << kaksi::laske() << std::endl; return 0; } ----------------------- Koska laiskuus on ilomme eikä toisto ole ihmisen työtä, ja tuohonhan menee kauhiasti aikaa, niin olemmepa keksineet tavan päästä eroon noista kahta kaksoispistettä edeltävästä nimiavaruuden määrehistä. ---[ namespace2.cpp ]--- #include //Käytetään nimiavaruutta std using namespace std; namespace yksi { int a; int b; int laske() { return a+b; } } namespace kaksi { double a; double b; double laske() { return a+b; } } int main (int argc, char** argv) { (void) argc; (void) argv; //Käytetään nimiavaruutta yksi using namespace yksi; //Ei tarvita määrittää nimiavaruutta, koska aiemmin on määritelty //tarvittava nimiavaruus käytettäväksi a=2; b=3; cout << laske() << endl; //Tässä nimiavaruus "kaksi" pitää määrittää kaksi::a=2.3; kaksi::b=3.4; cout << kaksi::laske() << endl; return 0; } ----------------------- Muttas, eikös nimiavaruuden kaksi voisi ottaa myös käyttöön? Muutetaan rivi "kaksi::a=2.3" muotoon "a=2.3" ja laitetaan sen eteen "using namespace kaksi;" ja kokeillaan. Kääntäjä valittaa, sillä se ei pysty selvittämään, kumpaan nimiavaruuteen "yksi" vai "kaksi" tuo muuttuja "a" kuuluu. Nimiavaruus on määritelty vain tietyssä komentolohkossa eli blockissa. Blokki määritellään kaarisuluilla { ja }. Edelliseen esimerkkiin voitaisiin määrittää blokki alkamaan ennen "using namespace yksi;" -riviä ja loppumaan ensimmäisen cout:n jälkeen. Ja toinen alkamaan ennen "kaksi::a=2.3;" -riviä ja loppumaan toisen cout:n jälkeen. Tällöin voitaisiin lisätä rivi "using namespace kaksi;" tuon blokin alkuun ja poistaa kaikki "kaksi::" -määreet. Sisällyttäminen ja otsikkotiedostot ----------------------------------- Koodiin voidaan ottaa mukaan toisten koodia ja yleensä näin tehdäänkin, sillä eihän pyörää kannata uudestaan keksiä. Toinen hyötyisyys on, jotta voidaan hajauttaa koodia usempaan hallittavaan, ja kenties jopa siirrettävään tai uudelleenkäytettävään, osaan. Syntaksi on: #include tai: #include "otsikkotiedosto" Eroa on se, että <> -merkkien välissä olevaa tiedostoa etsitään sisällytyspolun (include path) varrelta, kun taasen "" -merkkien välissä olevaa etsitään kyseisestä hakemistosta. C-kielessä (ja aikaisissa C++ -määrityksissä) otsikkotiedostojen tiedostopääte määriteltiin, tyyliin: #include Tämä pätee edelleen C++:ssa omille tiedostoille ja useille C:n otsikkotiedostoille, mutta C++:n omat erityiset otsikkotiedostot annetaan ilman päätettä. Esim. #include Toki on mahdollista vielä tehdä: #include Mutta tällöin käytetään C-yhteensopivaa otsikkotiedostoa. Joistakin C:n otsikkotiedostoista on C++ -versio. Nimi on muutoin sama kuin C:n otsikkotiedoston nimi, mutta edessä on c-kirjain ja pääte otetaan pois. Esim: #include C:ssä vastaava otsikkotiedosto olisi time.h Omien otsikkotiedostojen pääte voi olla esim. h, hxx tai hh. Ja tämä kappale olisi varmaan ollut järkevä olla jossain ennen nimiavaruus -esimerkkejä tai niiden välissä tai jossain. Noh, olkoon, jatketaan sitten tähän eli luodaan oma otsikkotiedosto. Olkoon se nimi.h ---[ nimi.h ]--- #ifndef _NIMI_H #define _NIMI_H namespace yksi { int a; int b; int laske() { return a+b; } } namespace kaksi { double a; double b; double laske() { return a+b; } } #endif ---------------- Nuo #-alkuiset rivit ovat esikääntäjän komentoja. Selitän ne varmaan joskus myöhemmin, mutta nyt jatketaan käyttöesimerkillä, joka esimerkillisesti käyttää tuota otsikkotiedostoa (nimesinpä sen muuten omalaatuisella tavalla, tai no ainakin melkein ): ---[ nimi.c ]--- #include #include "nimi.h" using namespace std; int main (int argc, char** argv) { (void) argc; (void) argv; using namespace yksi; a=2; b=3; cout << laske() << endl; } ---------------- Eli vanhaa juttua ja tuttua huttua. Joo, kääntämisessä ei tarvita mitään erikoista, kunhan tuo nimi.h löytyy sitten tuolta. Jos ei löydy niin kyllä se keksii siitä varoittaa. Ja jollei keksi niin syö keksi, jonka jälkeen yrität keksiä, että mikä tuossa meni vikaan. Jollet keksi, niin noniin nojoo. Joo. Vaihdetaanpa aihetta. Luokkakeskustelua ----------------- Puhutaanpa hieman luokista ja miksei myös luokattomuuksista. Luokkahan on rakenne, johon kuuluu joukko muuttujia ja metodeita. Nämä voivat lisäksi olla julkisia (public), yksityisiä (private) tai suojattuja (protected). Luokalla voipi olla luoja- ja tuhoajametodit (constructor, destructor). Niissä yleensä alustellaanpalustellaan luokan muuttujat ja luodaan tarvittavannäköisiä rakenteitansa. Kun luokasta luodaan olio (<== shiinä she nyt on eli siinä hän (naisesta) nyt on, olio on muutes object tuolla eräisellä kielellä) kutsutaan sen luojametodia, jonka jälkeen olio on valmis käytettäväksi. Siis näin lyhyesti, hieman kulmia hioen ja näyteikkunat rikkoen, sanottuna. Rakennellaanpa seuraavaksi luokka. Luokka kuvatkoon vaikka elefanttia. Jottei ihan tyhmää... eiku tyhjästä tarvitse elefanttiluokkaa tehdä, tehdään ensin vaikkapa eläin-luokka. Tosin äätä ei suosita, joten olkoon se elain-luokka. Tästä luokasta tullaan sitten periyttämään elehvantti, ja miksei toinenkin. Ja vaikka norsu, jos oiken geenitutkimus kiinnostaa. Tai no ei tuo "tutkimus" oikein sopiva sana ole. Ja ne jotka eivät tahdo heti näin suuren haasteen kimppuun voivat tehdä vaikka lampaan, siis lammas-luokan. Ne lampaat on varmaan myös mukavampia sitten sängyssä - siis laskea. Mutta ilman suurempia jorinoita minä menen nukkumaan ja te saatte askarrella hienot luokat. Kunhan herään niin voidaan katsoa, kuka on luokan paras luokantekijä. Noni, hopihopi, ei minulla ole koko päivää aikaa nukkua! Huoah! Elikkäs ette vai osaa? No pakko se on sitten näyttää. Pyh. Varmaan ois ollut kätevätäpätevätä selittää jotain tuota tehdävänantoista ennen. Noh, shelitellään nyt sitten. Elikkäsjolikkas luokka määritellään näin: Class luokannimi { private: //Tähän yksityiset ominaisuudet protected: //Tähän suojatut ominaisuudet public: //Tähän julkiset ominaisuudet luokannimi(); //Tämä onpi luojametodi, huomaa ettei paluuarvoa ~luokannimi(); //Tämä onpi tuhoajametodi, huomaa ettei paluuarvoa }; //Huomaappa puolipilkullisuus! Ja luokan toteutus tehdään sitten jonnekkii näie: paluuarvo luokannimi::metodinnimi() { toteutus } Ja luojat ja epäluojat näin (luojassa eipi ole tuota maanmatoista elikkäs tildeä elikkäs joo): luokannimi::~luokannimi() { } Periyttäminen tapahtuupi näie: Class luokannimi : public periytettavanluokannimi { } Tuossa tuo periyttäminen voipi tapahtua jokosta public, private tahi protected. Noista varmaan joskus kerron tarkemminparkemmin tai sitten en kerro, mutta kyse on siitä, notte miten tuo lapsokainen näkeepi ja pystyypi käyttämään äiteensä ominaisuuksia. Niinjoo nuilla ominaisuuksilla tarkoittanen muuttujia ja metodeitansa. Ja kun tuossa periyttämisessänsä ois kiva saada metodinsa kovattuva uudella versiolla, siis siellä lapsiluokassa, niin laitetaanpa metodinmäärittelyn eteen määre virtual, joka meinaapi, notta aijotaan se jokus sitten uuvelleenmääritellä. Ei sitä tietenkään pakko ole, mutta tuo mahdollistaapi sellaisen toiminteen. Sitten onki semmosia ystäväluokkia, ja noille ystäville sitten kerrotaan salaisuuksia ja tämmösiä. Elikkäs nuo voipi lukia noita ystäväluokkansa yksityisiä ja suojattuja ominaisuuksia. Tästäpä otamma esimekkilöisen. class Luokka1 { public: int nelio(Luokka2 k); } class Luokka2 { private: int a; public: void aseta(int n) {a=n;} friend class Luokka1; } int Luokka1::nelio(Luokka2 k) { //Tuo k.a on niinku tuolla ei-kovin-julkeana määritelty return k.a*k.a; } #include //Niinjoo, tämmönenkii on mahdollista, ei tarvii niistä parametrilöistä //huolehtia vaan antaapi niiden mennä /dev/null:n tai jonnekin muuhun //parempaan paikkahan int main() { Luokka1 a; Luokka2 b; b.aseta(3); std::cout << a.nelio(b) << endl; return 0; } Eiköhän siinä ollut jo tarpeheksi teoriaa, nyt sitten rotuerottelun vastakohtaisuus eli käytännössä opetetaan teoriaa käytännön esimerkkilöiden avullansa. Eli se taannoinen eläinluokka sekä se elehvantti. ---[ elain.h ]--- #ifndef _ELAIN_H #define _ELAIN_H #include #include using namespace std; //Eläin-luokka class Elain { private: //Yksityiset muuttujat ja metodit string vari; //Eläimen väri unsigned int jalat,ika; //Jalkojen määrä, ikä double paino,pituus; //Eläimen paino ja pituus public: //Julkiset muuttujat ja metodit Elain(); Elain(string vari, unsigned int jalat, unsigned int ika, double paino, double pituus); virtual ~Elain(); //Pirtuaalinen eli tämä voidaan korvailla aliluokissa virtual void aantele(); virtual void liiku(); unsigned int numJalat() { return jalat; } unsigned int getIka() { return ika; } double getPaino() { return paino; } double getPituus() { return pituus; } string getVari() { return vari; } double painoPerJalka(); }; #endif ----------------- ---[ elain.cpp ]--- #include "elain.h" //Luojametodi Elain::Elain(string vari, unsigned int jalat, unsigned int ika, double paino, double pituus) { if (paino<0) paino=0; if (pituus<0) pituus=0; //Tuo this on osoitin tähän eli tissiin eli kyseessä olevaan olioon, //eli tässä vaiheessa se olio on niinkuin jo olemassa ja nyt tämä //luojametodi on joutunut kutsuttavaksi, jolloin siis itseensä voi //viitata tuolla. Ja koska kyseessä on osoitin, pitää siellä //osoittimen osoittamassa oliossa olevaan ominaisuuteen viitata tuolla //nuolivermehellä. Ja this->vari on sama kuin (*this).vari eli joo. //Voitaisiin myös kutsua ilman this -vermettä, jos parametrina ei //olisi samannimistä vermettä, eli jos parametrina ois string ovari, //niin voitaisiin tehdä vari=ovari; this->vari=vari; this->ika=ika; this->jalat=jalat; this->paino=paino; this->pituus=pituus; } //Tyhmä luoja Elain::Elain() { vari=""; jalat=0; ika=0; paino=0; pituus=0; } //Tuhoajametodi Elain::~Elain() { } //Metodi, jossa laitetaan se eläin ääntelemään void Elain::aantele() { cout << "Eläin ääntelee" << endl; } //Metodi, jolla liikutetaan eläintä void Elain::liiku() { cout << "Eläin liikkuu" << endl; } //Veri pig kalkuleissön double Elain::painoPerJalka() { if (jalat>0) return paino/jalat; return 0; } ------------------- ---[ elefantti.h ]--- #ifndef _ELEFANTTI_H #define _ELEFANTTI_H #include "elain.h" //Elehvantti-luokka, joka periytetään Elain-luokasta, periyttämismetodi on //public eli Elain-luokan metodit ovat julkisia tälle aliluokalle. Mahdollista //olisi myöäs periyttää suojatusti (protected) tahis yksityisesti (private). //Yskityisesti periytetty ei mahdollista metodien korvaamista (julkisten //ominaisuuksien käytön kylläkin) eikä esim. tuota luojarakennetta, mitä tässä //on käytetty. class Elefantti : public Elain { public: Elefantti(string vari, unsigned int jalat, unsigned int ika, double paino, double pituus) : Elain (vari,jalat,ika,paino,pituus) {}; void aantele(); void liiku(); }; #endif --------------------- ---[ elefantti.cpp ]--- #include "elefantti.h" //Laitetaan elehvanttimme ääntelehtimään void Elefantti::aantele() { cout << "Töttörötöö" << endl; } //Ja liikutellaan sitä void Elefantti::liiku() { cout << "Elefantti liikkuu" << endl; } ----------------------- ---[ luokka.cpp ]--- #include #include "elain.h" #include "elefantti.h" int main(int argc, char** argv) { (void) argc; (void) argv; ///Luodaanpas eläimiä //Parametrithan on sitten: väri, jalat, ikä, paino, pituus Elain elio("sininen", 1, 1, 0.4, 0.2); Elain *otokka=new Elain("vihreä", 6, 5, 1.4, 1); Elain *elain=new Elain(); //Piippolan vaarilla oli talo, hiialahiialahei! Elefantti antti("harmaa", 4, 3, 1200, 1.5); Elefantti *dumbo = new Elefantti("tummanharmaa", 4, 5, 2000, 3.1); elio.aantele(); otokka->aantele(); elain->aantele(); elain = new Elefantti("vihreä", 4, 10, 3200, 4); antti.aantele(); dumbo->aantele(); elain->aantele(); //Tosi kova meno... (wouwouwou) dumbo->liiku(); elain->liiku(); otokka->liiku(); cout << dumbo->getPaino() << endl; cout << elain->getPaino() << endl; cout << otokka->getPaino() << endl; return 0; } -------------------- Ja nuiden kääntely voidaan helpoiten kai suoritella: g++ -Wall -W -o luokka luokka.cpp elain.cpp elefantti.cpp Nuo voidaan käännellä myös sitten erikseensä, jotta ei koko paskaa tarvi kerrallansa, jos joku yksi verme meneepi muuttumaan: g++ -c -Wall -W -o elain.o elain.cpp g++ -c -Wall -W -o elefantti.o elefantti.cpp g++ -c -Wall -W -o luokka.o luokka.cpp g++ -o luokka luokka.o elain.o elefantti.o Elikkä tuo mätsik parametri -c tekeepi tuosta ulosteesta objektitiedoston. Tuo viimeinen komento sitten linkittääpi nuo kaikki yhteen ja tekeepi ajettavuutta. Jos sitten vaikkapa menet elehvanttia (joko otsikkotiedostoa tai toteutusta) muuttamaan niin juoksutat vain tuon elehvantti- ja viimeisen rivin. Tästä päästäänkin tekotiedostoon eli Makefile:n. Yksinkertainen esimerkki: ---[ Makefile ]--- CC=g++ CXXFLAGS=-W -Wall all: luokka luokka: luokka.o elain.o elefantti.o luokka.o: luokka.cpp elain.o: elain.cpp elain.h elefantti.o: elefantti.cpp elefantti.h clean: rm -f *.o elain ------------------ Tuo toimii ainakin GNU Maken kanssa, mutta jonkun muun kanssa voi joutua määrittämään käännöskomennot. Tai jos pitää jotain erikoisempaa tehdä. Mutta syntaksi tuossa on: kohde: kohteeseen kuuluvat tiedostot mahdolliset käännöskomennot Tuo GNU Make on sen verta järkevä, notta se osaa tuon kohteen kasata tiedostojen perusteella, eli kun tuolla onpi joku.cpp niin se keksii, notta C++ -kääntäjää pitää käytellä. Tuo CC=g++ määrittää C-kääntäjän. Sitä tarvitaan tuossa luokka-kohteessa, kun ne on niitä objektitiedostoja kaikki ja kun niillensä on oletuskääntäjä gcc, joka on lähinnä C-kääntelijä ja voi sitentäten aiheuttaa hieman ongelmia C++ -koodin linkittäminen sillä, kun ei se osaa automaagisesti ottaa tarvittavia kirjastoja mukaan. Tosin -liostream auttaisi varmaan asiassa, tai ei välttämättä. Mutta esimerkki näinpä voisi tuon itse määritellä: luokka: luokka.o elain.o elefantti.o g++ -o $@ $^ luokka.o: luokka.cpp luokka.h g++ $(CXXFLAGS) -o $@ $< Ja niin edelleensä. Tuo $@ tarkoittaa kohdetta, $^ tarkoittaa kaikkia noita tiedostoja sen kohteen perässä ja $< vain sitä ensimmäisentapaista. Niin ja tämä ei ole Make -tutti, joten lopetetaanpa tuosta puhuminen. Aihe seuraava seuraavaksi tulee seuraamme. Kän juu sii mii? ---------------- Eli lyhvästi palataan C-aikakaudelle, ja kokeilemme, mitenkä tämä kaukonäkö on mahdollistaa tuon näkökielen metodien käytön. Onneksi jouluun on aikaa, jouluun on aikaa... Paljonkos sinne mahtaakaan olla aikaa? Laskeskellaanpa se sheuraavaksi. Siis tarvitaan ajanmittauslaite, joka kertoo nykyisen ajan, ajanmuodostamislaite, joka kertoo joulunajan. Ja tietenkin jotain hassua. Sitä hassua en tässä paljasta, mutta muut voin kertoa. Luodaanpa ajanmittauslaite: ---[ joulu.cpp ]--- #include //Palauttaa nykyisen ajan sekunteina epoch-ajasta mitattuna (man 2 time) time_t aika() { time_t tmp; time(&tmp); return tmp; } //Ja sitten vielä ajanmuodostamislaite. //Tuo vuosi on sitten nelinumeroinen elikkäs esim. 2003 time_t jouluAika(unsigned int vuosi) { struct tm joulu; //Keskiyö joulu.tm_sec=0; joulu.tm_min=0; joulu.tm_hour=0; //Jouluaattopäevä joulu.tm_mday=24; joulu.tm_mon=11; //Joku kuitenkin antaa väärän syöttehen if (vuosi>1900) joulu.tm_year=vuosi-1900; else joulu.tm_year=vuosi; //Tyhmyyksiä joulu.tm_wday=0; joulu.tm_yday=0; joulu.tm_isdst=0; return mktime(&joulu); } #include std::string laskeEro(time_t b, time_t a) { time_t ero=b-a; std::ostringstream tmp; bool meni=false; if (ero<0) { ero*=-1; tmp << "Joulu meni jo "; meni=true; } else tmp << "Jouluun on "; int paivat=ero/3600/24; tmp<< paivat << " päivää "; ero-=paivat*3600*24; int tunnit=ero/3600; tmp<< tunnit << " tuntia "; ero-=tunnit*3600; tmp<< ero/60 << " minutia ja " << ero%60 << " sekuntia"; if (meni) tmp << " sitten"; return tmp.str(); } #include int main() { time_t nyt,joulu; nyt=aika(); joulu=jouluAika(2003); std::cout << laskeEro(joulu,nyt) << std::endl; return 0; } Kun olet aluksi käsin laskeskellut ja sormia väännellyt, ihmeiteä miettinyt ja aivoasi solmuuun saattanut, niin voidaan todeta, notta huomenna on joulu! Vai eikös ollutkaan? Kun omaan laskupäähän tahi kirjanpitoon ei voida luottaa (kun noita sormiakin on vain 10 eikä 24 ja kun nyt on jo viides joulukalenterikin juotavana) niin käännelläänväännellään tuo sitten ja juosten kustaan, niin uloste pitäisi olla esimerkiksi: Jouluun on 12 päivää 0 tuntia 24 minutia ja 15 sekuntia Tahi jos olet kovin joulut unohdellut tahi edellisvuotta muistelet niin: Joulu meni jo 352 päivää 23 tuntia 36 minutia ja 7 sekuntia sitten Nyt sitten ne juulkuppet, tannenbaumit ja joulukuset nostetaan sieltä maasta ja laitetaan kunnolla painoa, notta ei heti meni kaatumaan kun joku keksii niitä halailla nautittuaan liiallismäärin kotoisaa viintä. Eikäs niitä kynttelikköjäkään kannata paljon kaadella kun voipi sitten taivas loimotella hieman siinä entisen talon paikalla juu. Ja kyllä ne kauppiaat kiittääpi ja kumartaapi kun sinne menee korttinsa kanssa Visaa vinguttelemaan. Tai no ollaan me nyt sen verta suvaitsevaisia, että Mattia, Pekkaa, Villeä ja Petteriäkin saa vingutella. Pimpelipompeli pom. Tosin eipäs ne kauppiaat kumartele, kiittää saattavat kiittämättömät, mutta pökköpökkö vaan itellensä joo. Tulipas muuten mieleen, että se jouluntapainen taitaa olla tulossa. Varmaan pitäis tehrä jotain. Esimerkiksi siivota. No emmäny niin hullu ole. Mutta joo, jos sitä nyt tän kerran vaikka imurois tai puhaltelis suuremmat villaiset koirat jonneki piiloon. Näinpä teemmä. Loppusanat ---------- Ja seuraava osa seuraa, kunhan etii, jaksaa, kerkeää tai pystyy. Siellä pitäisi varmaan puhua sitten jostain muista asioista kuin tässä osassa käsitellyistä asioista. Se olkoon seuraavan osan tavoite. Saas nähdä, opetanko uudelleen heimaailmat tai luokat seuraavassa osassa. Kirjoitanpa itselleni ylös tuon: Joro, älä kirjoita samoista asioista tutoriaalisarjan toisessa osassa, vaan kerro jostain muista asioista. Noin, enköhän muista. No toivotaan ainakin. Joo, se on moro! Nähkää hyvin ja pitkälle! Tutin salli sallimus, kansalla syvä tuohtumus, tuohi tulessa palanut, kansalle valon antanut, kansa suuri turhimus, turhuuden suuhun laittanut, lapsuuden tuttia lullannut, ei men! Copyright (c) ei vuonna yksi, eekä vuonna kaks, vuan herran vuonna kolome, Joro (Toi Joro olen niinkuin minä, eli mä, eli Jouni oikealta nimeltä, sukua nimeää Roivas, josta päästäänkin kotoisille sivuille, osoitteessa http://roivas.org. Sähköistä kustia voi poljetuttaa osoitteseen jroivas@iki.fi vaikkapa. Ei se pure, ainakaan kovin lujaa, kuten olettaa saattaisi.)