Olio-ohjelmoinnin perusteetluento 2: Johdanto C++ kieleen, Ensimmäinen C++ ohjelma, Olioiden elinkaari
Sami JantunenLTY/Tietotekniikan osasto
Sisältö Johdanto C++ kieleen
Taustatietoa C++ kielestä Johdanto C++ kielen syntaksiin Ensimmäinen C++ ohjelma
Olioiden elinkaari Olioiden syntymä Olioiden kuolema Olioiden elinkaaren määräytyminen
C++ Varsinkin teollisuudessa yleistynyt oliokieli C-kielestä lähinnä laajentamalla johdettu Suurin osa ANSI C -ohjelmista laillisia C++-ohjelmia Tuloskoodi usein varsin tehokasta Luokkakäsite & olio-ajattelu ->korkean tason
abstraktio Mahdollisuus myös matalan tason ohjelmointiin Helpohko oppia C:tä osaaville Helppo yhdistää C-kirjastoja C++:aan Kieli ei rajoita ohjelman toteutustapaa Staattinen (käännösaikainen) tyypitys ISO C++ -standardi tarjoaa työkaluja C:n puutteiden
kiertämiseen
C++ haittoja C:stä periytyvä turvattomuus Yhteensopivuus C:n kanssa ->ikäviä kompromisseja
oliopiirteissä Vaatii myös C-tuntemusta Runsaasti ansoja ohjelmoijalle Helppo kirjoittaa C++:aa C-mäisesti Kielessä ei kaikkialla pakollisia automaattisia
virhetarkastuksia->ohjelmien sekoamiset yleisiä C:n matalatasoisuus paistaa kielestä läpi myös
oliotasolla Periytymismekanismia käytettäessä sallii
toteutukset, jotka itse asiassa olioajattelun vastaisia Ei pakota olioajatteluun
C++ kielen ominaisuuksia perustana on C-kieli, johon on tehty joitakin lisäyksiä ja olio-
ohjelmointimekanismit sisältävät laajennus C-kielen laajennukset:
Kommentit Tyyppimuunnokset (type casting) Syöttö ja tulostus Muuttujien näkyvyys Nimiavaruudet (namespace) Aliohjelman esittely (function prototypes) Vakiomuuttujat (constant variables) Viittausmuuttujat (reference variables) Dynaaminen muistinvaraus Inline aliohjelmat Merkkijonojen käsittely
Ihan ensimmäinen C++ ohjelmaOhjelman runko#include <iostream.h>void main ( ) {// Muistappa tämä.// Tällainen pitää aina olla!
}
// Tämä pitäisi muuten tallettaa//johonkin .cpp tiedostoon
Ohjelman dokumentointiOhjelman dokumentointia sanotaan “kommentoinniksi”. Kääntäjä jättää kommentit huomioimatta
C++:ssa kaksi tapaa kommentoida!// Kommentoi loppurivin/* Näin kommentoidaan isompia lohkoja. Kommentointi loppuu kun vastaan tulee */
Tyyliopas kommentoinnista Kunkin lähdekooditiedoston alkuun olisi hyvä
kommentoida seuraavat asiat: Ohjelman nimi Tiedoston nimi Tekijän nimi Kuvaus tiedoston siältämistä asioista Kehitysympäristö Muutoshistoria
Kunkin funktion alkuun tulisi kommentoida seuraavat asiat Funktion nimi Kuvaus funktiosta Funktioon tuleva tieto Funktiosta lähtevä tieto
Älä kommentoi itsestäänselvyyksiä. Perussääntö: Kerro miksi ei miten
Esimerkki tiedoston otsikosta
/******************************************* Ohjelma: Hello World** Tiedosto: hello.CPP** Tekijä: Sami Jantunen** Kuvaus: Tulostaa näytölle sanat “Hello world” ** Ympäristö: Turbo C++ versio4, Windows 2000** Muutokset: 1.00 01.01.2004 (SJ) Ensimmäinen versio** 1.00 14.01.2004 (SJ) Laitettu iso kirjan World sanaan*************************************************/
Esimerkki funktion otsikosta
/************************************************** CircleArea --** This function calculates the area of a circle** with the specified radius.** Inputs: the circle's radius as a parameter** Output: the circle's area as the return value**************************************************/
Näytölle tulostus Käytä cout –oliota. Tarvitsee iostream luokkakirjastoa cout pystyy tulostamaan kutakuinkin mitä vaan.
Erityyppisiä muuttujia Useita asioita kerrallaan erikoismerkkejä kuten ‘\n’ (newline), ‘\a’ (alert), jne..
Useita asioita tulostetaan erottelemalla ne <<:llä endl:n avulla tehdään rivinsiirto Esimerkki:
#include <iostream>int main(void){string teksti = “Mitä häh?”;cout<<”Teksti: “<<endl<<teksti<<endl;return 0;
}
Käyttäjän syötteen lukeminen Käytä cin –oliota. Tarvitsee iostream luokkakirjastoa Lue syötteet muuttujiin >> avulla Esimerkki:
#include <iostream>int main(void){// Luodaan muuttuja
int myInt;
// Luetaan tietoa näppikseltäcin >> myInt;
// Ja tulostetaan näytölle cout << “myInt is “ << myInt << endl;
return 0;}
Sääntöjä luokkien luomisesta Luokat yleensä nimetään alkamaan isolla kirjaimella
Tidosto, jonne kirjoitat luokan pitäisi nimetä saman nimiseksi luokan kansa: <class name>.cpp
Esimerkki: Jos meillä on tarve luoda koirasta luokka annamme sille nimen Dog ja tallennamme sen tiedostoon Dog.cpp
Muista että C++ on case-sensitiivinen!
Nimeämiskäytännöistä Nimeämiskäytännöillä on huomattava
vaikutus luokkien käytettävyyteen. Tavoite: Luokat, funktiot ja muuttujat
tulisi nimetä niin, että koodi muistuttaa englanninkielistä tekstiä
Kullakin yrityksellä tyypillisesti omat tyylioppaat.
Esimerkki tyylioppaasta: Ellemtelin tyyliopas: http://www.doc.ic.ac.uk/lab/cplus/c++.rules/
Yksinkertaisin luokkaLuokan määre:class <class name> {};
Esimerkki: sinua pyydetään luomaan luokka X
ratkaisu:class X {};
Muista, että luokka pitäisi talletta X.cpp nimiseen tiedostoon (EI x.cpp!)
Ensimmäinen oliopohjainen ohjelma:Koirat eläintarhassa
luokka CDog: Attribuutit (ominaisuudet) vesikauhuinen vai ei (bool) paino (int or float) nimi (string)
Toiminnallisuus murise syö
CDogbool rabidOrNotint weightstring namevoid growl()void eat()
Haluamme määritellä koiraluokan ja luoda sen muikaisia koiraolioita eläintarhaan!
Vaihe 1: Luokan runkoclass CDog {// attribuutit tähän – nimi,
// paino, vesikauhuisuus
// toiminto tähän– murise, syö};
CDogbool rabidOrNotint weightstring namevoid growl()void eat()
Vaihe 2: Attribuutit(puhumme public-määreestä myöhemmin)
class CDog { public:bool rabidOrNot;int weight;string name;
// Toiminnallisuus tänne};
CDogbool rabidOrNotint weightstring namevoid growl()void eat()
Vaihe 3: Rakentaja (Constructor)Rakentaja on erityinen funktio
Sitä käytetään alustamaan uusi olioAktivoidaan kun joku luo uuden (esim. komento new) instanssin luokastasta
Ei noudata funktion standardi puumerkkiä
Rakentajan TÄYTYY olla saman niminen kuin luokkakin
Vaihe 3: Rakentajan suunnittelu Rakentajat eroavat toisistaan tarpeen mukaan Kysy itseltäsi:
Syntyvätkö kaikki CDog:t joko vesikauhuisena tai terveenä?(kyllä – kaikki koirat syntyvät terveinä)
Syntyvätkö kaikki CDog:t saman painoisena? (ei – koiranpennut on eripainoisia)
Onko kaikilla koiranpennuilla sama nimi? (ei – niillä on eri nimiä)
Jos vastaus kysymykseen on ei, silloin joudut toimittamaan kyseisen tiedon rakentajalle parametrinä
Vaihe 3: Rakentajaclass CDog {public:bool rabidOrNot;int weight;char name [255];// ConstructorCDog::CDog (int x, String y) {rabidOrNot = false;weight = x;strcpy (name, y);}// Toiminnallisuus tänne
};
Huomaa, että jokainenCDog minkä luomme syntyy terveenä. Paino ja nimi riippuu parametrien arvoista.
Vaihe 4: Rentoudu ja pidä taukoPalaamme murinaan ja syömiseen hieman myöhemmin
Meidän pitää vielä puhua:LukijametodeistaMuuttajametodeistaToimintaa toteuttavista metodeista
Leikitään ensin vähän aikaan ohjelman pääalgoritmilla
ZooNyt puhutaan ihan uudesta tiedostosta! (Zoo.cpp)
Zoo sisältää pääfunktion mainmain sisältää kontrolloivan algoritminAskeleet:
Luo runkoLuo muutamia olioita luokistaAla kertomaan mitä olioiden pitäisi tehdä
Vaihe 1: Luo runko#include <iostream.h>void main ( ) {
// Luo oliosi täällä!}
Vaihe 2: Määritä CDog osoitin#include <iostream.h>void main ( ) {
CDog *c1;}
// null means “dead”null
c1
new operaattorinew
Tuo oliot henkiin Kutsuu luokan rakentajaa (constructor)Varaa riittävästi muistia luotavalle oliolle
Formaatti:<class> *<var_name>;<var_name> = new <class> (<parameters>);
Vaihe 3: herätä c1 henkiin#include <iostream.h>void main ( ) {
CDog *c1;c1 = new CDog (14, “Bob”);
}
new kutsuu CDog:n rakentajaaCDog::CDog (int x, char y[ ]) {
rabidOrNot = false;weight = x;strcpy (name, y);
}
nullc1
Tee muistiin tilaa
nullc1
#include <iostream.h>void main ( ) {
CDog* c1;c1 = new CDog (14, “Bob”);
}
new kutsuu CDog:n rakentajaaCDog::CDog (int x, char y[ ]) {
rabidOrNot = false;weight = x;strcpy (name, y);
}
#include <iostream.h>void main ( ) {
CDog* c1;c1 = new CDog (14, “Bob”);
}
Tiedon välitystä
CDog::CDog (int x, char y[ ]) {rabidOrNot = false;weight = x; strcpy (name, y);
}
nullc1// Tämä tapahtuu CDog.cpp -tiedostossa
Tiedon välitystä
nullc1
rabid:falseweight:14
name:”bob”
#include <iostream.h>void main ( ) {
CDog* c1;c1 = new CDog (14, “Bob”);
}
CDog::CDog (int x, char y[ ]) {rabidOrNot = false;weight = x; strcpy (name, y);
}
// Tämä tapahtuu CDog.cpp -tiedostossa
#include <iostream.h>void main ( ) {
CDog* c1;c1 = new CDog (14, “Bob”);CDog c2 (7, “Ethel”);
}
Luo toinen CDog olio
nullc1
rabid:falseweight:14
name:”bob”
CDog::CDog (int x, char y[ ]) {rabidOrNot = false;weight = x; strcpy (name, y);
}
// Tämä tapahtuu CDog.cpp -tiedostossa
Tee muistiin tilaa
nullc1
rabid:falseweight:14
name:”bob”
c2
#include <iostream.h>void main ( ) {
CDog* c1;c1 = new CDog (14, “Bob”);CDog c2 (7, “Ethel”);
}
CDog::CDog (int x, char y[ ]) {
rabidOrNot = false;weight = x; strcpy (name, y);
}
// Tämä tapahtuu CDog.cpp -tiedostossa
Tiedon välitystä
nullc1
rabid:falseweight:14
name:”bob”
c2
#include <iostream.h>void main ( ) {
CDog* c1;c1 = new CDog (14, “Bob”);CDog c2 (7, “Ethel”);
}
CDog::CDog (int x, char y[ ]) {
rabidOrNot = false;weight = x; strcpy (name, y);
}
// Tämä tapahtuu CDog.cpp -tiedostossa
Kopioi arvon muistiin
nullc1
rabid:falseweight:14
name:”bob”
c2rabid:falseweight:7
name:”Ethel”
CDog::CDog (int x, char y[ ]) {rabidOrNot = false;weight = x; strcpy (name, y);
}
// Tämä tapahtuu CDog.cpp -tiedostossa
#include <iostream.h>void main ( ) {
CDog* c1;c1 = new CDog (14, “Bob”);CDog c2 (7, “Ethel”);
}
Takaisin CDog-luokan pariin
class CDog {public:bool rabidOrNot;int weight;char name [255];// ConstructorCDog::CDog (int x, char y[ ]) {rabidOrNot = false;weight = x;strcpy (name, y);}// Toiminnallisuus. Vielä pitäisi syödä ja murista
};
CDogbool rabidOrNotint weightstring namevoid growl()void eat()
Toiminnallisuuden toteuttaminen
void CDog::eat ( ) {cout << name << “ is now eating” << endl;weight++;
}
void CDog::growl ( ) {cout << “Grrrr” << endl;
}
CDogbool rabidOrNotint weightstring namevoid growl()void eat()
Lisää metodit luokkaanclass CDog {public:bool rabidOrNot;int weight;char name [255];// ConstructorCDog::CDog (int x, char y[ ]) {rabidOrNot = false;weight = x;strcpy (name, y);}void CDog::eat ( ) {
cout << name << “ is now eating” << endl;weight++;
}
void CDog::growl ( ) {cout << “Grrrr” << endl;
}};
CDogbool rabidOrNotint weightstring namevoid growl()void eat()
“.” ja “->” operaattorit “.” ja “->” operaattoreita käytetään jotta:
päästään käsiksi olion attribuutteihin päästään käsiksi olion metodeihin päästä ylipäätänsä olion sisuksiin käsiksi
“.” operaattoria käytetään konkreetiisten instanssien kanssa (ei siis pointtereissa)Formaatti:<instanssi>.<attribuutti tai metodi>
“->” operaattoria käytetään pointtereiden kanssaFormaatti:<instanssi> -> <attribuutti tai metodi>
“.” ja “->” operaattiorien käyttö#include <iostream.h>void main ( ) {CDog* c1;c1 = new CDog (14, “Bob”);CDog c2 (7, “Ethel”);c2.bark( );c1->growl( );}
Lukijametodit (accessors) ja muuttajametodit (modifiers) Lukijametodit
palauttaa olion atribuutin arvon ei voi olla paluuarvotyypiltään void metodissa pitää olla return komento
Muuttajametodit muuttavat olion attribuutin arvoa yleensä paluuarvotyyppinä void
Yleensä attribuutille on yksi lukijametodi ja yksi muuttajametodi (Get-Set metodit)
Lukija- ja muuttajametoditLukijametodi rabid attribuutillebool CDog::getRabid ( ) {return rabid;}
Muuttajametodi rabid attribuutillevoid CDog::setRabid (bool myBoolean) {rabid = myBoolean;}
Sijoita nämä CDog luokan sisälle
Lukija- ja muuttajametodien käyttö#include <iostream.h>void main ( ) {CDog* c1;c1 = new CDog (14, “Bob”);CDog c2 (7, “Ethel”);c1->setRabid (1);// prints 1 for truecout << c1->getRabid( ) << endl;
}
Valmista?Käännetään CDog.cppEi onnistunut! Mikä meni pieleen?
Korjataan CDog Käännetään uudestaan…
Valmista?Käännetään seuraavaksi Zoo.cpp
Pahoja ongelmia Missä vika????
Ongelman etsiminen…. Jotta ymmärtäisimme ongelman,
tutkitaanpa ensin hieman miten C++ kääntäjät toimivat
Käännetty vs. Tulkittu Tulkit tulkitsevat lähdekoodia “lennosta” ohjelmaa ajettaessa
Kääntäjät muuntavat lähdekoodin koneen ymmärtämään muotoon ennen ohjelman ajoa
Nopeus? Käännetty ohjelma voittaa
Turvallisuus, suoja viruksilta ym? Tulkittu ohjelma voittaa
Ohjelman kääntäminenKäytät jompaa kumpaa seuraavista:Komentorivi: g++ (Unixissa) IDE (Esim. Visual C++)
Työkaluista:Integrated Development Environments (IDE)
Helppo hallita suurempienkin ohjelmien kehitystä
Siisäänrakennettu kääntäjä ja linkkeri
Usein mukana online syntaksi oppaat
Paljon käyttäjäystävällisempi kuin komentorivi
Visual C++ on esimerkki IDE:stä
Käännösprosessi (Compiling)
Muuntaa lähdekoodin luettavasta muodosta (.cpp) tietokoneen ymmärtämään muotoon (.obj)
Yhdistää (Link) käännetyn koodin liittämiisi kirjastoihin
Tuloksena ajettava tiedosto (.exe)
Käännös
Hyvä tietää C++ käännösprosessista C++ kääntäjä käsittelee kutakin
lähdekooditiedostoa kerrallaan yhtenä kokonaisuutena.Jos käännettävä tiedosto sisältää viittauksia
luokkiin, mitkä on kuvattu jossain toisessa tiedostossa, ne pitäisi jollain lailla “esitellä” käännöksen aikana
Ratkaistaan ongelma eriyttämällä luokan esittely ja konkreettinen toteutus eri tiedostoihin
Otsikkotiedostot (Header files) Sisältävät luokan esittelyn Tiedoston nimi on muotoa
LuokanNimi.h Otsikkotiedostoon voi kirjoittaa
avoimien (public) funktioiden toteutuksia. Tätä kannattaa käyttää hyväksi yksinkertaisten get-set metodien yhteydessä
Otsikkotiedostot (Header files) Otsikkotiedostot otetaan käyttöön seuraavilla tavoilla:
#include “MinunLuokka.h” #include <iostream.h>
#-symboli kertoo, että komento on tarkoitettu esikäsittelijän suoritettavaksi. (esikäsittely tapahtuu ennen varsinaista käännöstä)
#include –komento kertoo, että kääntäjä käsittelee käännettävää tiedostoa aivan kun #include rivin tilalla olisi kyseisen tiedoston koko sisältö
Hakasulut (<>) kertovat, että otsikkotiedosto sijaitsee kääntäjän omissa hakemistoissa
Lainausmerkit kertovat, että kyseessä on ohjelmoijan tarjoama otsikkotiedosto
Kumpi tuli ensin: Muna vai kana?
MunaKana *äiti
KanaMuna *lapsi
Ongelmia luokkien esittelyssä kun luokat viittaavat toisiinsa (mutual recursion)
Viittaus toiseen luokkaan voidaan hyväksyä luokan jäsenmuuttujaksi vain jos kääntäjä on jo tietoinen luokasta.
Ratkaisuna ongelmaan on etukäteen julkaisu (forward declaration):
Moninkertainen otsikkotiedoston esittely
Suurissa ohjelmissa #include-rakenteet tulevat mutkikkaiksi ja monitasoisiksi.
Kääntäjä voi tällöin virheellisesti saada käsittelyyn saman otsikkotiedoston useaan kertaan. Virhetilanne
tietokanta.h
#include “paivays.h”
loki.h
#include “paivays.h”
paivays.h
Pääohjelma
#include “tietokanta.h”#include “loki.h”
Esikäääntäjän ehdollinen kääntäminen
#ifndef C_MODUULI_H#define C_MODUULI_H
typedef struct paivays data { int p , k , v ;}paivays_PVM;
paivays_PVM paivays_luo( int paiva, int kuukausi, int vuosi ); paivays_tulosta( paivays_PVM kohde );#endif /* C_MODUULI_H */
Ehdollisella kääntämisellä (#ifndef X #define X … #endif) voidaan varmistaa, että esittely näkyy kääntäjälle vain yhden kerran
#include käytöstä vielä Vältä otsikkotiedostojen sisällyttämistä
muihin otsikkotiedostoihin Ihanteellisessa tilanteessa
otsikkotiedostoon pitää sisällyttää vain kyseisen luokan kantaluokan otsikkotiedosto
Muita pakollisia sisällytettäviä tiedostoja ovat niiden luokkien otsikkotiedostot, jonka tyyppisiä jäsenmuuttujia luokassa on
Jos vain viitataan toisen tyyppiseen olioon pelkkä luokan esittely riittää
Linkkaus
Milloin kannattaisi kääntää?Nyrkkisääntö: Mitä epävarmempi olet, sitä useammin käännätTämä nopeuttaa virheiden paikallistamisessa
Käännä:Ohjelman rungon luomisen jälkeen Joka 5-10 uuden rivin jälkeen
Älä kuitenkaan tule riippuvaiseksi kääntäjästä!
Palataanpa takaisin koirat eläintarhassa esimerkkiin
Luodaan erillinen otsikkotiedosto CDog luokalle
Tiedoston nimi CDog.h Vielä paljon kerrottavaa
Ei vielä ihan tyylipuhdas luokan esittely.
class CDog {public:
int weight;bool rabid;char name [ ];CDog (int x, char y[ ]);bool getRabid ( );void setRabid (bool x);char [ ] getName ( );void setName (char z[ ]);int getWeight ( );void setWeight (int x);void bark( );void growl( );
};
CDogbool rabidOrNotint weightstring namevoid growl()void eat()
Meidän lopullinen CDog.cpp
#include <iostream.h>#include <CDog.h>// ConstructorCDog::CDog (int x, char y[ ]) {rabid = false;weight = x;strcpy(name, y);}void CDog::eat ( ) {cout << name << “ is eating”;
}void CDog::growl ( ) {cout << “Grrrr”;}
bool CDog::getRabid ( ) { return rabid;}void CDog::setRabid (bool x) { rabid = x;}int CDog::getWeight ( ) { return weight;}void CDog::setWeight (int y) { weight = y;}char[ ] CDog::getName ( ) { return name;}void setName (char z[ ]) { name = z;}
Joko nyt? Oikeastaan ei.
Kalvon koodissa oli pieniä kirjoitusvirheitä . Löydätkö virheitä aiemmista kalvoista?
Ohessa korjauksen jälkeen onnistunut käännös
Ensimmäinen C++ ohjelmaYhteenveto Luokkia luodessa
1. Luo luokan runko2. Määrittele attribuutit3. Kirjoita luokalle rakentaja4. Kirjoita toiminnallisuus (mukaanlukien lukija-
muuttajametodit) Kirjoita luokat eri tiedostoihin Olioiden ominaisuuksia käytetään “.”
and “->” operaattorien avulla
Missä mennään? Johdanto C++ kieleen
Taustatietoa C++ kielestä Johdanto C++ kielen syntaksiin Ensimmäinen C++ ohjelma
Olioiden elinkaari Olioiden syntymä Olioiden kuolema Olioiden elinkaaren määräytyminen
Olioiden syntymä Olioiden luomiseen saattaa liittyä
monimutkaisiakin toimenpiteitä kuten esim: Muistin varaaminen toisten olioiden luominen rekisteröitymiset (palvelinolio rekisteröi syntyessään
palvelunsa järjestelmänlaajuiseen tietokantaan) resurssien käyttöönotto (tietokantaolio avaa
syntyessään tietokannan sisältävän tiedoston ja lukeen sen muistiin)
muut toimenpiteet (ikkunaolio piirtää syntyessään ikkunan ruudulle)
Alustustoimenpiteet sysätään kunkin olion omalle vastuulle
Olioiden kuolema Kun oliota ei tarvita enää, tulisi
suorittaa siivoustoimenpiteitä kuten: muistin vapautus toisten olioiden tuhuaminen rekistereistä poistuminen resurssien vapauttaminen
Siivoustoimenpiteet ovan myöskin kunkin olion omalla vastuulla
Olioiden elinkaaren määräytyminen Staattinen elinkaari Dynaaminen elinkaari
Staattinen elinkaari Oliot, jotka luodaan tavallisen muuttujan tapaan.
Esim: CDog c2(7, “Ethel”);
Kissa minunKissa; Olion syntymä- ja tuhoutumishetki on määrätty jo
käännösaikana. Kääntäjä osaa suorittaa automaattisesti luomis- ja
tuhoamistoimenpiteet Suositeltava tapa. Ei vaaraa muistivuodoista. Silti mahdollisuus vakaviin virheisiin .
Esimerkki: Osoittimet saattavat viitata automaattisesti tuhottavaan olioon
Muista, että olio tuhoutuu automaattisesti tietyssä kohdassa ohjelmaa
Dynaaminen elinkaari Olion elinkaaresta huolehtiminen on
jätetty ohjelmoijan vastuulle Olio ei tuhoudu automaattisesti. Se
täytyy erikseen tuhota. Pakko käyttää tilanteissa, missä olion
elinkaari ei rajaudu tiettyyn osaa koodia Mitä luot new -komennolla, sen tuhoat delete –komennolla
Vaarallista! Muistivuotoja tulee helposti
Tyypillisiä dynaamiseen luontiin liittyviä ongelmia Unohdetaan tuhota olio(t) Olio tuhotaan kahteen kertaan Tuhotun olion käyttäminen Taulukkojen tuhoaminen
Ohjeita virheiden välttämiseen Määrittele, kuka “omistaa” olion ja on täten
vastuussa sen tuhoamisesta Pyri siihen, että dynaamisesti luotuja olioita ei
käytetä monen eri tahon toimesta. Nollaa osoittimet tuhoamisen jälkeen
Paivays *p = new Paivays;//käytetään päiväystädelete p; p =0;
Tämä estää tilanteen, missä muuttujat viittaavaat muistipaikkaan, missä tuhotun olion ylitse ei ole kirjoitettu vielä uutta tietoa (Zombie-olioiden käyttö)
Kirjoita koodia, mikä pyrkii toipumaan virhetilanteista (Tästä lisää myöhemmissä luennoissa)
Rakentaja (constructor) Tunnetaan myös nimellä Muodostaja Tehtävänä hoitaa kaikki uuden olion
alustamiseen liittyvät toimenpiteet Jäsenmuuttujien alustus Olioon kuuluvien olioiden ulkopuolisten
tietorakenteiden ja olioiden luominen Olion rekisteröiminen jonnekkin
Kolmenlaisia rakentajia: oletusrakentaja (default constructor)
olion tietojäsenten alustus oletusarvoilla kopiointirakentaja (copy constructor)
olion tietojäsenten alustus toisesta oliosta kopioimalla parametrilliset rakentajat
Rakentaja (constructor) Sääntöjä Kääntäjä luo jokaiselle luokalle
oletusrakentajan ellei sellaista ole itse ohjelmoitu
Rakentajan nimi täytyy olla sama kuin luokan nimi
Rakentaja ei palauta paluuarvoa Rakentajalle ei voi määritellä
paluuarvon tyyppiä (ei edes void)
Oletusrakentaja (default constructor) Esimerkki
Pvm::Pvm(){time_t sek;tm *paiva;time(&sek);paiva = localtime(&sek);pp = paiva->tm_mday;kk = paiva->tm_mon + 1;vv = paiva->tm_year;
}
Rakentaja (constructor) Sijoitus vs. Alustuslista
Pvm::Pvm(const int p_pp, const int p_kk, const int p_vv): pp(p_pp), kk(p_kk), vv(p_vv){//Ei mitään tehtävää täällä
}
Pvm::Pvm(const int p_pp, const int p_kk, const int p_vv){
pp = p_pp;kk = p_kk;vv = p_vv;
}
Jäsenmuuttujiin sijoitus
Alustuslistan käyttö
Rakentaja (constructor) Sijoitus vs. Alustuslista Useinmiten sijoituksen ja alustuslistan käytöllä ei ole
lopputuloksessa eroa Vakioita ja viittauksia ei kuitenkaan voi alustaa
sijoittamalla. Alustuslistaa käyttämällä tämän voi tehdä. Esimerkki:
class ConstRef {public:
ConstRef( int ii);private:
int i;const int ci;int &ri;
};
ConstRef:: ConstRef (int ii ){ // sijoitus i=ii //OK ci=ii //error: ei voi sijoittaa vakioon ri=i; //error: ri ei ole alustettu}
Alustuslistaa käyttäen:ConstRef:: ConstRef (int ii )
:ci(ii),ri(i) //alustus{ i=ii;}
Sijoitusta yrittäen:Luokan kuvaus:
Kopiorakentaja (copy constructor) Saa parametrina viitteen olemassa
olevaan saman luokan olioon. Tehtävänä luoda identtinen kopio
parametrina saadusta oliosta Kääntäjä kutsuu sitä automaattisesti
tilanteissa, missä kopion luominen on tarpeen.
Jos kopiorakentaja puuttuu, se luodaan kääntäjän toimesta automaattisesti
Asiasta puhutaan lisää myöhemmillä luennoilla
Parametrillinen rakentaja Tarjoaa luokan luojalle mahdollisuuden määritellä
kullekkin luotavalle oliolle alustettavat arvot Parametrillinen rakentaja on aina ohjelmoijan
kirjoittama Samat säännöt kuin oletusrakentajalla. Poikkeus:
Parametrillisen rakentajan parametreihin voidaan määritellä tarvittaessa myös oletusarvot (näistäkin lisää myöhemmissä luennoissa)
Luokka voi sisältää useita erilaisilla parametreilla varustettuja rakentajia
Kääntäjä ei generoi oletusrakentajaa, jos ohjelmoija on määritellyt luokkaan parametrillisen rakentajan
Parametrillinen rakentajaEsimerkki
#include <iostream>#include <time>class Pvm{
int pp, kk, vv;public:
Pvm(const int, const int, const int); //parametrillinen rakentaja
void nayta()const;};Pvm::Pvm(const int p_pp, const int p_kk, const int p_vv): pp(p_pp), kk(p_kk), vv(p_vv){}void Pvm::nayta() const{
cout<<pp<<'/'<<kk<<'/'<<vv;}int main(void){
Pvm Paiva(3, 3, 96); //Paiva-olion luonti ja alustus
Paiva.nayta();return 0;
}
Purkaja Tunnetaan myös nimellä Hajoitin Purkajan nimi on ~Luokan_nimi Purkaja ei saa parametreja Purkajalla ei ole tyyppiä, ei saa palauttaa
mitään arvoa Esittelyssä ei saa esiintyä varattuja sanoja
const, volatile tai static Purkaja voi olla (suositellaan!!) virtuaalinen
(liittyy periytymiseen, tästä puhutaan lisää myöhemmin)
Purkaja Purkajaa kutsutaan automaattisesti seuraavissa tilanteissa:
Poistutaan koodialueesta, missä olio on luotu staatisella tavalla Tuhotaan olio delete -komennolla
On mahdollista (mutta hyvin harvinaista) kutsua purkajaa eksplisiittisesti. Purkajan kutsu:
Olio.Luokka:: ~Luokka();Olio->Luokka:: ~Luokka();
Purkajan esittely:class Luokka...public:
~Luokka();};
Purkajan määrittely:Luokka:: ~Luokka(){
tietojäsenten tyhjennys}
Purkaja esimerkki#include <iostream.h>#include <time.h>class Pvm{
int pp, kk, vv;public:
Pvm(const int, const int);Pvm(const Pvm &, const int = 96);Pvm &Pvm::operator=(const Pvm &);~Pvm();void Nayta() const;
};Pvm::Pvm(const int p_pp, const int p_kk) :
pp(p_pp), kk(p_kk){
time_t sek;tm *paiva;time(&sek);paiva = localtime(&sek);vv = paiva->tm_year;
}Pvm::Pvm(const Pvm &p_pvm, const int p_vv) :
pp(p_pvm.pp), kk(p_pvm.kk){
vv = p_vv;}Pvm &Pvm::operator=(const Pvm &p_pvm){
pp = p_pvm.pp;kk = p_pvm.kk;vv = p_pvm.vv;return (*this);
}
Pvm::~Pvm(){
pp = 0;kk = 0;vv = 0;
}void Pvm::nayta() const{
cout<<pp<<'/'<<kk<<'/'<<vv;}int main(void){
cout<<"Päivä1: ";Pvm Paiva1(1, 2);Paiva1.Pvm::~Pvm();Paiva1.nayta();
cout<<"\nPäivä2: ";Pvm *Paiva2 = new Pvm(2, 2);Paiva2->Pvm::~Pvm();Paiva2->nayta();delete Paiva2;return 0;
}