Sami Jantunen LTY/Tietotekniikan osasto

Preview:

DESCRIPTION

Olio-ohjelmoinnin perusteet luento 2: Johdanto C++ kieleen, Ensimmäinen C++ ohjelma, Olioiden elinkaari. Sami Jantunen LTY/Tietotekniikan osasto. Sisältö. Johdanto C++ kieleen Taustatietoa C++ kielestä Johdanto C++ kielen syntaksiin Ensimmäinen C++ ohjelma Olioiden elinkaari - PowerPoint PPT Presentation

Citation preview

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 dokumentointi

Ohjelman 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ö

CDog

bool rabidOrNotint weightstring name

void growl()void eat()

Haluamme määritellä koiraluokan ja luoda sen mukaisia koiraolioita eläintarhaan!

Vaihe 1: Luokan runko

class CDog {

// attribuutit tähän – nimi,

// paino, vesikauhuisuus

// toiminto tähän– murise, syö

};

CDog

bool rabidOrNotint weightstring name

void growl()void eat()

Vaihe 2: Attribuutit(puhumme public-määreestä myöhemmin)

class CDog {

public:

bool rabidOrNot;

int weight;

string name;

// Toiminnallisuus tänne

};

CDog

bool rabidOrNotint weightstring name

void growl()void eat()

Vaihe 3: Rakentaja (Constructor)

Rakentaja on erityinen funktioSitä 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ä tauko

Palaamme 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 operaattori

newTuo 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 rakentajaa

CDog::CDog (int x, char y[ ]) {

rabidOrNot = false;

weight = x;

strcpy (name, y);

}

null

c1

Tee muistiin tilaa

null

c1

#include <iostream.h>

void main ( ) {

CDog* c1;

c1 = new CDog (14, “Bob”);}

new kutsuu CDog:n rakentajaa

CDog::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);

}

null

c1// Tämä tapahtuu CDog.cpp -tiedostossa

Tiedon välitystä

null

c1

rabid:false

weight: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

null

c1

rabid:false

weight: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

null

c1

rabid:false

weight: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ä

null

c1

rabid:false

weight: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

null

c1

rabid:false

weight:14

name:”bob”

c2rabid:false

weight: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

};

CDog

bool rabidOrNotint weightstring name

void growl()void eat()

Toiminnallisuuden toteuttaminen

void CDog::eat ( ) {

cout << name << “ is now eating” << endl;

weight++;

}

void CDog::growl ( ) {

cout << “Grrrr” << endl;

}

CDog

bool rabidOrNotint weightstring name

void 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;

}};

CDog

bool rabidOrNotint weightstring name

void 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ään 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.cpp

Ei 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äminen

Kä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 ei 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

C++ ohjelman rakenne

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 rabidOrNot

int weight

string name

void growl()void eat()

Meidän lopullinen CDog.cpp

#include <iostream.h>

#include <CDog.h>

// Constructor

CDog::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

Esimerkki(asetetaan d1 osoittamaan d2:n osoittamaan olioon)

Dog *d1, *d2;d1 = new Dog (5, “bob”);d2 = new Dog (5, “bob”);

d1 = d2;

null

d1 d2

rabid=false

weight=5

name=“bob”

rabid=false

weight=5

name=“bob”

Muistin tarkastelu

Nyt, d1 ja d2 osoittavat samaan olioon

Mutta mikään ei osoita tähän

Koska mikään osoitin ei osoitaa kyseiseen olioon, emme voi enää käyttää sitä.

Kutsumme tätä osaa muistiaroskaksi. Teimme oliosta käyttökelvottoman!

null

d1 d2

rabid=false

weight=5

name=“bob”

rabid=false

weight=5

name=“bob”

Muistin vapauttamisesta

Muista vapauttaa käyttämätön muistialue

Sääntö: Älä koskaan vain aseta osoitinta null:ksi

Muista käyttää delete ( ) operaattoria

Esim: delete d1;

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;

}

Recommended