Esittely latautuu. Ole hyvä ja odota

Esittely latautuu. Ole hyvä ja odota

Olio-ohjelmoinnin perusteet luento 3: Muuttujista ja funktioista Sami Jantunen LTY/Tietotekniikan osasto.

Samankaltaiset esitykset


Esitys aiheesta: "Olio-ohjelmoinnin perusteet luento 3: Muuttujista ja funktioista Sami Jantunen LTY/Tietotekniikan osasto."— Esityksen transkriptio:

1 Olio-ohjelmoinnin perusteet luento 3: Muuttujista ja funktioista Sami Jantunen LTY/Tietotekniikan osasto

2 VAROITUS!!!!!!  Tällä luennolla käsitellään perusasioita, joilla ei ole sinänsä paljon tekemistä oliopohjaisuuden kanssa!  Esimerkit ovat esitetty ei-oliopohjaisella tavalla!  Tarkoitus on vain havainnoillistaa perusasioita  Toivottavasti osaat soveltaa näitä jatkossa, kun puhumme lisää luokista!

3 Sisältö  Muuttujat  Muuttujien määrittely ja tietotyypit  Osoittimet (pointer variables)  Viittaukset (reference variables)  Funktiot  määrittely ja tiedon välitys  muuttujien näkyvyysalueet  oletusparametrit (default parameters)  uudelleenmäärittely (overloading)  avoimet funktiot (inline)  rekursio

4 Muuttujista…  Tietokoneohjelmat manipuloivat tietoa  Meillä on siis tarve tallettaa tietoa  Muuttujat:  Kuvaavat tiettyä muistin muistipaikkaa  Sisältävät tietyn tiedon  Kun määrittelet muuttujan:  Varaat tilaa muistista  Annat muuttujalle nimen  Teet tämän kerran kutakin muuttujaa kohden

5 Kuinka luodaan muuttuja  Formaatti: ;  Muuttujan nimeksi voit valita melkein mitä vaan  Esimerkkejä: byte age; float gpa; String name; char letterGrade;  Nyt kun muuttujat on määritelty  Ei tarvitse määritellä niitä uudestaan  Voit käyttää muuttujan nimeä jatkossa tiedon lukemisessa ja kirjoittamisessa agegpa nameletterGrade

6 C++ varatut sanat  Näitä sanoja et kuitenkaan voi käyttää sellaisenaan muuttujien nimissä:  auto, bool, break, case, catch, char, class, const, continue, default, do, double, else, enum, extern, float, for, friend, goto, if, inline, int, long, namespace, new, operator, private, protected, public, register, return, short, signed, sizeof, static, struct, switch, template, this, throw, try, typedef, union, unsigned, void, volatile, while

7 Sallittuja muuttujien nimiä  Ei saa olla varattu sana  Ei saa alkaa numerolla  Ei saa sisältää symboleja kuten  Kuten #, @, !, ^, &, /, ), tai välilyönti  Poikkeukset: alaviiva _, ja dollarimerkki $  Esimerkkejä: byte $theValue; // legal char test_value;// legal double double;// not legal int rum&coke;// not legal bool true or false; // not legal for two reasons!

8 Muuttujien alustamisesta  Tarkoittaa alku-arvon antamista muuttujalle  Arvo annetaan = -operaattoria käyttäen  Kopioi oikealla olevan tiedon vasemmalla määriteltyyn muuttujaan. Muuttujan tyyppi ja tiedon formaatti pitää olla yhteensopiva  Alustettava tieto voi olla vakio tai toinen muuttuja!  Esimerkkejä: int age; age = 15; char letterGrade = ‘B’; char yourGrade = letterGrade;

9 Tietotyypeistä  Tietotyyppi kertoo mikälaista tietoa muuttuja voi pitää sisällään  Jotkin tietotyypit ovat “sisäänrakennettuja” ohjelmointikieleen  Voimme myös määritellä tietotyyppejä itse (puhutaan tästä lisää myöhemmin)  Erilaisia tietotyypin muotoja:  Yksinkertaiset tietotyypit  Monimutkaiset tietotyypit (Valmistettu yksinkertaisista tietotyypeistä)

10 Yksinkertaiset tietotyypit  Sisäänrakennettuja  Numerot:  short (melko pienet numerot)  int (suuret numerot)  long (TOSI suuret numero)

11 Yksinkertaiset tietotyypit  Desimaaliluvut:  float (Ei niin tarkka kuin double )  double (Aika tarkka)  Tarkkuudella tarkoitetaan kuinka monta lukua tulee desimaalin jälkeen  Muita:  char (pitää sisällään merkkejä kuten ‘a’, ‘A’, ‘1’, ‘ ‘)  bool (pitää sisällään totuusarvon true tai false)

12 No kuinka iso se on?: sizeof( )  sizeof( ) kertoo kuinka paljon tietotyyppi vie tavuja  Esimerkki: int myInt; cout << sizeof (myInt) << endl;

13 Etumerkki vai ei?  C++ -kielessä voi määritellä merkki- ja kokonaislukumuuttujat etumerkittömäksi.  Etumerkillisille ja etumerkittömille kokonaisluvuille varataan yhtä paljon muistitilaa  Luvut ovat oletusarvoisesti etumerkillisiä  Etumerkittömän edut:  Etumerkittömät tietotyypit voivat siis sisältää suuremman joukon positiivisia lukuja

14 Perustietotyypeistä  Se kuinka monta tavua kukin tietotyyppi vie tilaa on riippuvainen kääntäjästä ja käyttöjärjestelmästä!  Tässä kuitenkin esimerkkejä: TyyppiKoko (Tavua)Arvoalue unsigned int20 …65535 int2-32768 … 32767 unsigned long40 … 4294967295 long4-2147483648 … 2147483647 char10 … 255

15 Miksi koolla on väliä  Eri tietotyypit vie eri määrän tavuja muistista  Et voi laittaa suuritilaisemman muuttujan sisältöä pienenpään muistitilaan: short s = 5;long l = 5; long l = s;short s = l; long short long short long short

16 Tyyppimuunnos (Type Casting)  Tapahtuu kun pistät jotain enemmän tilaa vievää tietoa pienempään muistipaikkaan  Tiedon tarkkuustaso saattaa heikentyä  Formaatti: = ( ) ;  Esimerkki: long myLong = 17; short myShort = (short)myLong; // We are squeezing myLong into myShort!

17 short vai long?  Jos on pienikin mahdollisuus, että muuttujaan tulee suurempi arvo kun tietotyyppiin mahtuu, niin valitse isompi tietotyyppi.

18 Etumerkittömien kokonaislukujen ylivuoto Sama ilmiö kun auton matkamittarissa:  Kun on maksimi saavutettu, niin aloitetaan uudestaan nollasta  Esim: unsigned int myInt = 65535; cout<< myInt << endl; myInt++; cout<< myInt << endl; myInt++; cout<<myInt<<endl; Mitä tapahtuu?? Tuloste: 65535 0 1

19 Etumerkillisten kokonaislukujen ylivuoto  Suurinta positiivista arvoa seuraa negatiivisin mahdollinen arvo  Esim: int myInt = 32767; cout<< myInt << endl; myInt++; cout<< myInt << endl; myInt++; cout<<myInt<<endl; Mitä tapahtuu?? Tuloste: 32767 -32768 -32767

20 unsigned short int luku1; unsigned short int luku2; unsigned short int luku3;  Liian työlästä?  Altis kirjoitusvirheille  Ratkaisuehdotuksia:  unsigned short int luku1, luku2, luku3;  Peitenimi (Typedef)

21 Peitenimi (Typedef) #include typedef unsigned short int USHORT; void main() { USHORT Leveys = 5; USHORT Korkeus;...

22 Yhteenveto  Muuttujat ovat nimettyjä soluja tietokoneen muistissa  On olemassa erilaisia tietotyyppejä säilyttämään erilaista tietoa  Eri tietotyypit vievät eri määrän tilaa  Kaikki muuttujat määritellään samanlaisen formaatin mukaisesti

23 Vakioista Miksi vakioita? -Esimerkki  Oletetaan, että koulussa yhdelle luokalle hyväksytään 15 oppilasta  Oletetaan myös, että koulun oppilasmäärä saadaan kerrottuna luokkien lukumäärä luokan koon kanssa: int luokkienLukumaara = 20; int oppilasmaara = 15 * luokkienLukumaara;  Mikä vikana?

24 Miksi vakioita?  Emme halua kovakoodata arvoja  Työlästä muuttaa  Emme myöskään halua laittaa pysyviä arvoja muuttujiksi  Joku saattaa vahingossa muuttaa niiden arvoja

25 #define  Hieman vanhentunut tapa määritellä vakio  Esim: #define oppilaitaLuokassa 15  Ei ota mitään kantaa tietotyyppiin  Esikäsittelijä pelkästään korvaa tekstin toisella  Kääntäjä siis näkee oppilaitaLuokassa-tekstin sijasta luvun 15

26 Const  Suositeltavampi tapa #define sijasta: const unsigned short int oppilaitaLuokassa = 15;  Kääntäjä tietää minkä tyyppinen vakio on.  osaa valvoa, että vakiota käytetään oikein  Vakiota ei voi muuttaa ohjelman suorituksen aikana. Jos vakio halutaan muuttaa, tulee ohjelma kääntää uudestaan.

27 Luetellut vakiot  Muuttujatyyppi, johon voi sijoittaa vain etukäteen määriteltyjä arvoja.  Luetellun vakion määrittely: enum VARI {PUNAINEN, SININEN, VIHREA, VALKOINEN, MUSTA};  Määrittelee sanan VARI  Määrittelee vakiot PUNAINEN (arvo 0), SININEN (arvo 1),…  Kullekin vakiolle on mahdollista määritellä arvo erikseen: enum VARI {PUNAINEN=100, SININEN, VIHREA=500, VALKOINEN=501, MUSTA=700};  Lause tuottaa vakiot PUNAINEN=100, SININEN=101, VIHREA=500, VALKOINEN=501 ja MUSTA=700  Luetellut vakiot ovat käteviä kun käsitellään rajallista alijoukkoa (värejä, viikonpäiviä, ym…)  Ohjelmakoodin luettavuus paranee

28 Luetellut vakiot käyttöesimerkki #include void main() { enum Paivat {Sunnuntai, Maanantai, Tiistai, Keskiviikko, Torstai, Perjantai, Lauantai }; Paivat vapaaPaiva; cout << “Minkä päivän haluat pitää vapaata (0-6)? “; cin >> vapaaPaiva; if (vapaaPaiva == Sunnuntai || vapaaPaiva == Lauantai) cout << “Viikonloppu on vapaata muutenkin!” << endl; else cout << “OK. pannaan asia muistiin” << endl; }

29 Lisäys- ja vähennysoperaattorit  ++ korottaa muuttujan arvoa yhdelläint counter = 5; counter++;counter = counter +1;  += korottaa muuttujan arvoa annetulla määrälläint counter = 5; counter += 3;counter =counter + 3;  Sama kuin Huomaa: Myös --, -=, %=, /= versiot

30 Lisäys- ja vähennysoperaattorit  Voidaan toteuttaa joko etuliitteenä (esim. ++counter; ) tai jälkiliitteenä (esim. counter++; )  Lopputulos sama paitsi jos muuttujaa käytetään samassa tilanteessa vielä arvon muutoksen lisäksi johonkin muuhun

31 Etu- ja jälkiliitelisäys esimerkki … void main() { int minunIka=40; int sinunIka=40; cout << “minun ikä on:” << minunIka++ << endl; cout << “sinun ikä on:” << ++sinunIka << endl; … } Mitä tapahtuu?? Tuloste: minun ikä on:40 Sinun ikä on:41

32 Osoittimista Oletteko nähneet tällaista?  Ohjelmoijan painajainen!  Johtuu osoittimen väärinkäytöstä

33 Tarkastellaan hieman muistia! Muistin osoite sinisellä, Arvot mustalla, Muuttujan nimi punaisella 1 -4717 2 -901 3 76 4 -0 5 98131 6 -1038 7 -554 8 7462 9 312 10 -6 11 3619 12 -4717 13 60981 14 4148 15 86851 16 -5155 17 95151 18 -47 19 2251 20 0 21 -78781 22 -901 23 -6 24 6720 25 -4717 26 -19 27 21511 28 -9 29 17 30 -6561 31 -651 32 9 33 761 34 -896761 35 7851 36 -6 37 9996 38 674547 39 -6868 40 41 5431 42 -4717

34 Määritetään int-muuttuja int myInt; 1 -4717 2 -901 3 76 4 -0 5 98131 6 -1038 7 -554 8 7462 9 312 10 -6 11 3619 12 -4717 13 60981 14 4148 15 86851 16 -5155 17 95151 18 -47 19 2251 20 0 21 -78781 22 -901 23 -6 24 6720 25 myInt -4717 26 -19 27 21511 28 -9 29 17 30 -6561 31 -651 32 9 33 761 34 -896761 35 7851 36 -6 37 9996 38 674547 39 -6868 40 41 5431 42 -4717

35 Mitä tuli tehtyä?  Määrittelemällä int- muuttujan varasimme juuri ja juuri tarpeeksi muistitilaa int- luvulle  Ei hajuakaan missä päin muistia muuttujamme sijaitsee  Tietokone valitsee muistiosoitteen “satunnaisesti”  Mikä arvo on varaamassamme muistipaikassa?  Voimmeko tulostaa muistipaikan arvon?  Tulostaisimme –4717! (roskaa)

36 Kopioidaan 42 muuttujan osoittamaan muistipaikkaan myInt = 42; 1 -4717 2 -901 3 76 4 -0 5 98131 6 -1038 7 -554 8 7462 9 312 10 -6 11 3619 12 -4717 13 60981 14 4148 15 86851 16 -5155 17 95151 18 -47 19 2251 20 0 21 -78781 22 -901 23 -6 24 6720 25 myInt26 -19 27 21511 28 -9 29 17 30 -6561 31 -651 32 9 33 761 34 -896761 35 7851 36 -6 37 9996 38 674547 39 -6868 40 41 5431 42 -4717 42

37 Osoittimet (Pointers)  Antaa mahdollisuuden päästä käsiksi muistipaikkaan, missä tieto sijaitsee  Osoitin määritellään seuraavanlaisella formaatilla: * ;  Example: int *ptr;

38 Määritetään int osoitin int *ptr; 1 -4717 2 -901 3 76 4 -0 5 ptr 98131 6 -1038 7 -554 8 7462 9 312 10 -6 11 3619 12 -4717 13 60981 14 4148 15 86851 16 -5155 17 95151 18 -47 19 2251 20 0 21 -78781 22 -901 23 -6 24 6720 25 myInt 42 26 -19 27 21511 28 -9 29 17 30 -6561 31 -651 32 9 33 761 34 -896761 35 7851 36 -6 37 9996 38 674547 39 -6868 40 41 5431 42 -4717

39 Mitä nyt tuli tehtyä?  Loimme uuden muuttujan joka tulee osoittamaan int tietoon  Huomaa, että osoitin ei vielä osoita myInt sisältämään tietoon  Mitä jos yritämme tulostaa osoittimen sisällön?

40 cout << ptr; (ptr:n arvoksi tulostuu 98131) 1 -4717 2 -901 3 76 4 -0 5 ptr 98131 6 -1038 7 -554 8 7462 9 312 10 -6 11 3619 12 -4717 13 60981 14 4148 15 86851 16 -5155 17 95151 18 -47 19 2251 20 0 21 -78781 22 -901 23 -6 24 6720 25 myInt 42 26 -19 27 21511 28 -9 29 17 30 -6561 31 -651 32 9 33 761 34 -896761 35 7851 36 -6 37 9996 38 674547 39 -6868 40 41 5431 42 -4717

41 Ongelma  Kuinka voimme saada myInt osoitteen, jotta ptr voi osoittaa sinne?  Muista, että voimme vielä käyttää myInt:ä suoraankin int someInt = myInt;  Tarvitsisimme todella keinon tallettaa myInt-muuttujan osoite  Meidän ei tarvitse tallettaa myInt muuttujan arvoa osoittimeen (vain osoite)

42 & operaattori  & operaattorin avulla päästään käsiksi muuttujan muistiosoitteeseen  Mitä seuraavanlainen komentorivi tulostaisi näytölle? cout << &myInt << endl;

43 Mitä tapahtuisi? cout << &myInt; 1 -4717 2 -901 3 76 4 -0 5 ptr 98131 6 -1038 7 -554 8 7462 9 312 10 -6 11 3619 12 -4717 13 60981 14 4148 15 86851 16 -5155 17 95151 18 -47 19 2251 20 0 21 -78781 22 -901 23 -6 24 6720 25 myInt 42 26 -19 27 21511 28 -9 29 17 30 -6561 31 -651 32 9 33 761 34 -896761 35 7851 36 -6 37 9996 38 674547 39 -6868 40 41 5431 42 -4717

44 Laitetaan osoitin osoittamaan…  Nyt pitäisi laittaa “ptr” osoittamaan myInt -muuttujan sisältämään tietoon ptr = &myInt; ptr on osoitin, joten se olettaa että sen arvoksi annetaan osoite & -operaattorilla saamme myInt muuttujan osoitteen ja kopioimme osoitetiedon ptr-osoittimen arvoksi

45 Ennen 1 -4717 2 -901 3 76 4 -0 5 ptr 98131 6 -1038 7 -554 8 7462 9 312 10 -6 11 3619 12 -4717 13 60981 14 4148 15 86851 16 -5155 17 95151 18 -47 19 2251 20 0 21 -78781 22 -901 23 -6 24 6720 25 myInt 42 26 -19 27 21511 28 -9 29 17 30 -6561 31 -651 32 9 33 761 34 -896761 35 7851 36 -6 37 9996 38 674547 39 -6868 40 41 5431 42 -4717

46 Jälkeen ptr = &myInt; 1 -4717 2 -901 3 76 4 -0 5 ptr 25 6 -1038 7 -554 8 7462 9 312 10 -6 11 3619 12 -4717 13 60981 14 4148 15 86851 16 -5155 17 95151 18 -47 19 2251 20 0 21 -78781 22 -901 23 -6 24 6720 25 myInt 42 26 -19 27 21511 28 -9 29 17 30 -6561 31 -651 32 9 33 761 34 -896761 35 7851 36 -6 37 9996 38 674547 39 -6868 40 41 5431 42 -4717

47 Mitäs tämä tekisi? ptr = myInt; 1 -4717 2 -901 3 76 4 -0 5 ptr 98186 6 -1038 7 -554 8 7462 9 312 10 -6 11 3619 12 -4717 13 60981 14 4148 15 86851 16 -5155 17 95151 18 -47 19 2251 20 0 21 -78781 22 -901 23 -6 24 6720 25 myInt 42 26 -19 27 21511 28 -9 29 17 30 -6561 31 -651 32 9 33 761 34 -896761 35 7851 36 -6 37 9996 38 674547 39 -6868 40 41 5431 42 -4717

48 Hupsista! ptr = myInt; 1 -4717 2 -901 3 76 4 -0 5 ptr 42 6 -1038 7 -554 8 7462 9 312 10 -6 11 3619 12 -4717 13 60981 14 4148 15 86851 16 -5155 17 95151 18 -47 19 2251 20 0 21 -78781 22 -901 23 -6 24 6720 25 myInt 42 26 -19 27 21511 28 -9 29 17 30 -6561 31 -651 32 9 33 761 34 -896761 35 7851 36 -6 37 9996 38 674547 39 -6868 40 41 5431 42 -4717

49 Osoittimen sisältö  Kuinka osoittimen sisältö saadan luettua?  Käytetään *-operaattoria  Esimerkki: cout << *ptr << endl; //Tulostaa ptr-osoittimen //osoittaman muistipaikan sisällön!

50 Seurataan osoitinta ja tulostetaan cout << *ptr << endl; 1 -4717 2 -901 3 76 4 -0 5 ptr 25 6 -1038 7 -554 8 7462 9 312 10 -6 11 3619 12 -4717 13 60981 14 4148 15 86851 16 -5155 17 95151 18 -47 19 2251 20 0 21 -78781 22 -901 23 -6 24 6720 25 myInt 42 26 -19 27 21511 28 -9 29 17 30 -6561 31 -651 32 9 33 761 34 -896761 35 7851 36 -6 37 9996 38 674547 39 -6868 40 41 5431 42 -4717

51 Toinen esimerkki Muistin osoite sinisellä, Arvot mustalla, Muuttujan nimi punaisella 1 -4717 2 -901 3 76 4 -0 5 98131 6 -1038 7 -554 8 7462 9 312 10 -6 11 3619 12 -4717 13 60981 14 4148 15 86851 16 -5155 17 95151 18 -47 19 2251 20 0 21 -78781 22 -901 23 -6 24 6720 25 -4717 26 -19 27 21511 28 -9 29 17 30 -6561 31 -651 32 9 33 761 34 -896761 35 7851 36 -6 37 9996 38 674547 39 -6868 40 41 5431 42 -4717

52 Määritetään osoitin int *ptr; 1 -4717 2 -901 3 76 4 -0 5 98131 6 -1038 7 -554 8 7462 9 312 10 ptr -6 11 3619 12 -4717 13 60981 14 4148 15 86851 16 -5155 17 95151 18 -47 19 2251 20 0 21 -78781 22 -901 23 -6 24 6720 25 -4717 26 -19 27 21511 28 -9 29 17 30 -6561 31 -651 32 9 33 761 34 -896761 35 7851 36 -6 37 9996 38 674547 39 -6868 40 41 5431 42 -4717

53 Mitä tapahtuu? cout << *ptr << endl; 1 -4717 2 -901 3 76 4 -0 5 98131 6 -1038 7 -554 8 7462 9 312 10 ptr -6 11 3619 12 -4717 13 60981 14 4148 15 86851 16 -5155 17 95151 18 -47 19 2251 20 0 21 -78781 22 -901 23 -6 24 6720 25 -4717 26 -19 27 21511 28 -9 29 17 30 -6561 31 -651 32 9 33 761 34 -896761 35 7851 36 -6 37 9996 38 674547 39 -6868 40 41 5431 42 -4717

54 Hupsista!

55  Parametrin välittäminen funktioon lähettää kopion alkuperäisestä muuttujan arvosta. Funktio ei siis pysty manipuloimaan pääohjelmassa olevan muuttujan arvoa! void cannotChange (int x) { x = 6; cout << x << endl; } void main ( ) { int myInt = 17; cannotChange (myInt); cout << myInt << endl; } Miksi käyttäisin osoittimia? 012 345 678 -291571 -29910-33 4161

56 void cannotChange (int x) { x = 6; cout << x << endl; } void main ( ) { int myInt = 17; cannotChange (myInt); cout << myInt << endl; } Märitetään myInt 012 345 678 -291571 -29910-33 4117 myInt

57 Kutsutaan funktiota 012 345 678 -291571 -29910-33 4117 myInt void cannotChange (int x) { x = 6; cout << x << endl; } void main ( ) { int myInt = 17; cannotChange (myInt); cout << myInt << endl; }

58 Kopiointi tapahtuu tässä! 012 345 678 -217571 -29910-33 4117 myInt x void cannotChange (int x) { x = 6; cout << x << endl; } void main ( ) { int myInt = 17; cannotChange (myInt); cout << myInt << endl; }

59 Ja muutos kohdistuu vain paikalliseen kopioon 012 345 678 -26571 -29910-33 4117 myInt x void cannotChange (int x) { x = 6; cout << x << endl; } void main ( ) { int myInt = 17; cannotChange (myInt); cout << myInt << endl; }

60 Tulostetaan kopion arvo (6) 012 345 678 -26571 -29910-33 4117 myInt x void cannotChange (int x) { x = 6; cout << x << endl; } void main ( ) { int myInt = 17; cannotChange (myInt); cout << myInt << endl; }

61 Palataan pääfunktioon (tulostetaan 17) (x on tuhottu, tilalle jäi vain roskaa…) 012 345 678 -26571 -29910-33 4117 myInt void cannotChange (int x) { x = 6; cout << x << endl; } void main ( ) { int myInt = 17; cannotChange (myInt); cout << myInt << endl; }

62 void canChange (int* x) { *x = 6; cout << *x << endl; } void main ( ) { int myInt = 17; int* ptr = &myInt; canChange (ptr); cout << myInt << endl; } Ja sitten sama osoittimilla 012 345 678 -26571 -29910-33 41412

63 Luodaan myInt 012 345 678 -26571 -29910-33 4117 myInt void canChange (int* x) { *x = 6; cout << *x << endl; } void main ( ) { int myInt = 17; int* ptr = &myInt; canChange (ptr); cout << myInt << endl; }

64 Luodaan ptr osoittamaan myInt:n muistipaikkaan 012 345 678 76571 -29910-33 4117 myInt ptr void canChange (int* x) { *x = 6; cout << *x << endl; } void main ( ) { int myInt = 17; int* ptr = &myInt; canChange (ptr); cout << myInt << endl; }

65 Lähetetään ptr:n kopio funktioon 012 345 678 76571 -29910-33 4117 myInt ptr void canChange (int* x) { *x = 6; cout << *x << endl; } void main ( ) { int myInt = 17; int* ptr = &myInt; canChange (ptr); cout << myInt << endl; }

66 Lähetetään ptr:n kopio funktioon 012 345 678 767 -29910-33 4117 myInt ptr x void canChange (int* x) { *x = 6; cout << *x << endl; } void main ( ) { int myInt = 17; int* ptr = &myInt; canChange (ptr); cout << myInt << endl; }

67 Muutetaan x:n osoittamaa muistipaikkaa 012 345 678 767 -29910-33 4117 myInt ptr x void canChange (int* x) { *x = 6; cout << *x << endl; } void main ( ) { int myInt = 17; int* ptr = &myInt; canChange (ptr); cout << myInt << endl; }

68 Muutetaan x:n osoittamaa muistipaikkaa 012 345 678 767 -29910-33 416 myInt ptr x void canChange (int* x) { *x = 6; cout << *x << endl; } void main ( ) { int myInt = 17; int* ptr = &myInt; canChange (ptr); cout << myInt << endl; }

69 Tulostetaan x:n osoittaman muistipaikan sisältö (6) 012 345 678 767 -29910-33 416 myInt ptr x void canChange (int* x) { *x = 6; cout << *x << endl; } void main ( ) { int myInt = 17; int* ptr = &myInt; canChange (ptr); cout << myInt << endl; }

70 Huomaa pääohjelmassa, että muuttujan arvo on muuttunut (myöskin 6) 012 345 678 767 -29910-33 416 myInt ptr x void canChange (int* x) { *x = 6; cout << *x << endl; } void main ( ) { int myInt = 17; int* ptr = &myInt; canChange (ptr); cout << myInt << endl; }

71 Mielenkiintoinen juttu Seuraavat lausekkeet tulostavat saman asian! cout << *ptr << endl; cout << myInt << endl Samoin nämä! cout << ptr << endl; cout << &myInt << endl; MIKSI? void canChange (int* x) { *x = 6; cout << *x << endl; } void main ( ) { int myInt = 17; int* ptr = &myInt; canChange (ptr); cout << myInt << endl; }

72 Vakio-osoittimet  Varattu sana const voidaan kirjoittaa osoitinmuuttujan määrittelyyn ennen tietotyyppiä, tietotyypin jälkeen tai molempiin paikkoihin.  Esim:  const int * pYksi;  int * const pKaksi;  const int * const pKolme;

73 Vakio-osoittimet esimerkit  pYksi osoittaa int- tyyppiseen vakioon. Osoittimen osoittamaa tietoa ei voi muuttaa mutta osoittimen arvoa voi muuttaa  const int * pYksi;  pKaksi on osoitinvakio, joka osoittaa int-tyyppiseen tietoon. Osoittimen osoittamaa int- tietoa voi muuttaa mutta osoittimen arvoa ei voi muuttaa  int * const pKaksi;  pKolme on osoitinvakio int –tyyppiseen vakioon. Psoittimen osoittamaa tietoa ei voi muuttaa eikä myöskään osoittimen arvoa voi muuttaa  const int * const pKolme;

74 Osoittimet yhteenveto  Sinun tulee ymmärtää miten muisti toimii, jotta ymmärtäisit osoittimien toiminnan  &-operaattori on osoittimiin liittyvien salaisuuksien äiti   Osoittimien sisällön saat * -operaattorilla  Voit manipuloida funktioissa pääohjelman muuttujien arvoja välittämällä funktiokutsussa osoittimia.

75 Viittausmuuttujat (Reference variables)  Osoittimia tarvitaan, mutta ne ovat usein pahojen ohjelmointivirheiden lähde.  Viittausmuuttujat:  osoittaa osoittimen tapaan olemassa olevaan tietoon.  on osoittimia turvallisempi. Kääntäjä tekee viittausmuuttujalle enemmän tarkistuksia  on osoittimia selkeämpi käyttää  Viittausmuuttuja on kuin “peitenimi” tai “synonyymi” olemassa olevalle tiedolle

76 Viittausmuuttujat Käyttöohje  Muodostetaan &-operaattorilla seuraavan formaatin mukaisesti: & = viitattavaTieto; ESIM: int &jokuViittaus = jokuInt;  Viittaus on AINA alustettava johonkin tietoon ennen käyttöä!  Viittausta ei voi muuttaa viittaamaan toiseen kohteeseen!

77 Viittausmuuttuja käyttöesimerkki Mitä tapahtuu?? void main () { int intYksi; int &jokuViittaus = intYksi; intYksi = 5; cout << ”intYksi: ” << intYksi << endl; cout << ”jokuViittaus: ” << jokuViittaus << endl; jokuViittaus = 7; cout << ”intYksi: ” << intYksi << endl; cout << ”jokuViittaus: ” << jokuViittaus << endl; } Tulostus: intYksi: 5 jokuViittaus: 5 intYksi: 7 jokuViittaus: 7

78 Tiedon välitys funktion parametreissä  Funktioita koskee kaksi rajoitusta  parametrit ovat kopioita alkuperäisistä tiedoista (muistathan osoitin-esimerkin)  funktio voi palauttaa vain yhden paluuarvon  Rajoitukset voi kiertää joko osoittimella tai viittauksella

79 Palataan osoitinesimerkin pariin Vaikeaselkoinen (*-operaattorin jatkuva käyttö) void canChange (int* x) { *x = 6; cout << *x << endl; } void main ( ) { int myInt = 17; int* ptr = &myInt; canChange (ptr); cout << myInt << endl; }

80 Ja sitten sama viittauksia käyttäen void canChange (int &x) { x = 6; cout << x << endl; } void main( ) { int myInt = 17; canChange (myInt); cout << myInt << endl; } Selkeämpää vai mitä????

81 Useampi paluuarvo funktiolle? Viittaustoteutus int kertoja (int luku, int &nelio, int &kuutio ) { nelio = luku*luku; kuutio = luku*luku*luku; return 1; //onnistui } void main( ) { int luku, nelio, kuutio, tulos; tulos = kertoja(luku, nelio, kuutio); } Miten tämä toteutettaisiin osoittimilla????

82 Sama osoitintoteutuksena int kertoja (int luku, int *nelio, int *kuutio ) { *nelio = luku*luku; *kuutio = luku*luku*luku; return 1; //onnistui } void main( ) { int luku, nelio, kuutio, tulos; tulos = kertoja(luku, &nelio, &kuutio); }

83 Parametrien välittäminen kopioina vs. viittauksena -Tehokkuusnäkökulma  Parametrien välittämisestä kopioina seuraa taustalla seuraavia tehtäviä:  kutsutaan välitettävän tyypin kopiorakentajaa  sijoitetaan kopio muistiin  kutsutaan välitettävän tyypin purkajaa  kutsutaan paluutyypin kopiorakentajaa  kutsutaan paluutyypin purkajaa  Jos välitettävät parametrit ovat suurikokoisia olioita, kopiointi syö muistia ja suorituskykyä   suositaan osoittimia ja viittauksia? (Entäpä turvallisuus ja olion eheys?)

84 Vakioviittaus  Voidaan tehokkaasti välittää funktion parametreina olioita ja olla samalla varma siitä, että oliota ei muuteta

85 Milloin käytetään viittauksia? Milloin osoittimia?  Suosi viittauksia:  ulkoasultaan selkeämpiä  turvallisempia  Viittausta ei voi muuttaa! Käytä osoitinta jos:  on tarve osoittaa ensin yhteen tietoon ja sitten toiseen  on tarve väliillä osoittaa tietoon, mutta välillä ei

86 Missä mennään?  Muuttujat  Muuttujien määrittely ja tietotyypit  Osoittimet (pointer variables)  Viittaukset (reference variables)  Funktiot  määrittely ja tiedon välitys  muuttujien näkyvyysalueet  oletusparametrit (default parameters)  uudelleenmäärittely (overloading)  avoimet funktiot (inline)  rekursio

87 Termiologiaa  Funktio on looginen ryhmä komentoja  Uudelleenkäytettävää koodia  Kirjoita kerran  Kutsu niin monta kertaa kuin haluat  Edut  Uudelleenkäytettävää  Helpottaa abstraktiota  Vähentää monimutkaisuutta  Vähentää koodin pituutta

88 Miksi funktioita? (näetkö mitään toistuvaa?) double average; int userNum1, userNum2; cout << “Please enter the 2 numbers” << endl; cin >> userNum1; cin >> userNum2; average = (userNum1 + userNum2) / 2; … // a lot of other code cin >> userNum1; cin >> userNum2; average = (userNum1 + userNum2) / 2; … cin >> userNum1; cin >> userNum2; average = (userNum1 + userNum2) / 2;

89 Miksi funktioita? (näetkö mitään toistuvaa?) Samaa koodia double average; int userNum1, userNum2; cout << “Please enter the 2 numbers” << endl; cin >> userNum1; cin >> userNum2; average = (userNum1 + userNum2) / 2; … // a lot of other code cin >> userNum1; cin >> userNum2; average = (userNum1 + userNum2) / 2; … cin >> userNum1; cin >> userNum2; average = (userNum1 + userNum2) / 2;

90 Siirretään toistuva koodi omaksi funktioksi cin >> userNum1; cin >> userNum2; average = (userNum1 + userNum2) / 2; Tee tästä oma funktio double average; int userNum1, userNum2; cout << “Please enter the 2 numbers” << endl; cin >> userNum1; cin >> userNum2; average = (userNum1 + userNum2) / 2; … // a lot of other code cin >> userNum1; cin >> userNum2; average = (userNum1 + userNum2) / 2; … cin >> userNum1; cin >> userNum2; average = (userNum1 + userNum2) / 2;

91 Ja nimetään sitten funktio cin >> userNum1; cin >> userNum2; average = (userNum1 + userNum2) / 2; double calculateAverage( ) double average; int userNum1, userNum2; cout << “Please enter the 2 numbers” << endl; cin >> userNum1; cin >> userNum2; average = (userNum1 + userNum2) / 2; … // a lot of other code cin >> userNum1; cin >> userNum2; average = (userNum1 + userNum2) / 2; … cin >> userNum1; cin >> userNum2; average = (userNum1 + userNum2) / 2;

92 Ja kutsutaan funktiota (sen sijaan että duplikoisimme komentoja) double average; int userNum1, userNum2; cout << “Please enter the 2 numbers” << endl; average = calculateAverage(); … // a lot of other code average = calculateAverage(); … average = calculateAverage(); cin >> userNum1; cin >> userNum2; average = (userNum1 + userNum2) / 2; average = calculateAverage();

93 Mitä taas tuli tehtyä?  Kirjoitimme koodin kerran, mutta käytimme sitä useasti  Lyhensimme rivien määrää koodissamme  Yksinkertaistimme koodia  Tämä on proseduraalista abstraktiota (muistathan ensimmäisen luennon?)

94 Muuttujien näkyvyysalue (Scope)  Näkyvyysalue- kuka näkee ja mitä  Funktion sisällä määritetyt muuttujat näkyvät vain funktion sisäpuolella!  Pitää olla keino lähettää informaatiota funktiolle  Pitää olla keino saada tietoa funktiosta  Esimerkki: function1 ei näe myInt:iä function1function2 char myChar; int myInt;

95 Globaalit muuttujat  Funktioiden ulkopuolella määritellyt muuttujat (globaalit muuttujat) näkyvät kaikissa funktioissa (myös main:ssa)  Paikallinen muuttuja, joka on saman niminen kuin globaali muuttuja ei vaikuta globaalin muuttujan arvoon  itse asiassa paikallinen muuttuja “piilottaa” saman nimisen globaalin muuttujan #include //globaaleja muuttujia int x =5, y =7; void main() { cout << “Y on ” << y << endl; omaFunktio(); cout << “Y on ” << y << endl; } void omaFunktio() { y = 10; }

96 Varoituksen sana globaaleista muuttujista  Globaalien muuttujien käyttö on vaarallista!  Kukaan ei pidä kirjaa niiden käytöstä  Yksi käyttää yhdellä tavalla, toinen toisella  altis virheille, mitä on vaikea paikallistaa  Globaalien muuttujien käyttöön ei yleensä ole tarvetta  Malta vielä hetki. Seuraavilla luennoilla tulee lisää (=oliopohjaisia) ratkaisuja muuttujien näkyvyysongelmaan

97 Funktion malli unsigned short int LaskeAla (int pituus, int leveys) Paluuarvon tyyppiFunktion nimiParametrien nimet Parametrien tyypit

98 Paluutyyppi  Funktiolla on mahdollisuus palauttaa kutsuvaan koodiin tietoa  Jos funktio ei palauta mitään, sen paluutyyppi on void  Jos funktio palauttaa tietoa, sen paluutyyppi on samaa tyyppiä kun palautettavan tiedon tyyppi  Esimerkkejä paluutyypeistä:  int  char  bool

99 Tehtävä: Valitse paluutyypit  Funktion nimi average getLetterGrade areYouAsleep getGPA printMenu getStudentName  Paluutyyppi double or float char boolean double or float void String

100 Funktion nimestä  Samoja sääntöjä kuin muuttujienkin nimeämisessä. Voi olla melkein mitä tahansa paitsi:  varattu sana  Ei voi alkaa numerolla  ei voi sisältää symboloja paitsi _ ja $  Funktion nimi tulisi alkaa pienellä kirjaimella  Jos funktion nimessä on useita sanoja aloita ne (ensimmäistä lukuunottamatta) isolla kirjaimella. Esimerkki:  thisIsAnExample  Tyypillisesti yrityksillä on omat tyylioppaat, joissa on määritelty nimeyskäytännöt tarkemmin!

101 Esimerkki #include void main ( ) { int num1, num2, num3; double result1, result2; num1 = 5; num2 = 7; num3 = 4; result1 = average (num1, num2); result2 = average (num3, num1); } double average (int x, int y) { return ( (x+y) / 2); } x y // Note: the average function is currently inactive

102 Esimerkki (määritä muuttujat) num1 num2num3 ? ?? #include void main ( ) { int num1, num2, num3; double result1, result2; num1 = 5; num2 = 7; num3 = 4; result1 = average (num1, num2); result2 = average (num3, num1); } double average (int x, int y) { return ( (x+y) / 2); } x y // Note: the average function is currently inactive

103 Esimerkki (määritä muuttujat) num1 num2num3 result1result2 ? ?? ?? #include void main ( ) { int num1, num2, num3; double result1, result2; num1 = 5; num2 = 7; num3 = 4; result1 = average (num1, num2); result2 = average (num3, num1); } double average (int x, int y) { return ( (x+y) / 2); } x y // Note: the average function is currently inactive

104 Esimerkki num1 num2num3 result1result2 5 74 ?? #include void main ( ) { int num1, num2, num3; double result1, result2; num1 = 5; num2 = 7; num3 = 4; result1 = average (num1, num2); result2 = average (num3, num1); } double average (int x, int y) { return ( (x+y) / 2); } x y // Note: the average function is currently inactive

105 Example (call the function) num1 num2num3 result1result2 5 74 ?? HERÄTYS! #include void main ( ) { int num1, num2, num3; double result1, result2; num1 = 5; num2 = 7; num3 = 4; result1 = average (num1, num2); result2 = average (num3, num1); } double average (int x, int y) { return ( (x+y) / 2); } x y

106 Example (data passing) num1 num2num3 result1result2 5 74 ?? #include void main ( ) { int num1, num2, num3; double result1, result2; num1 = 5; num2 = 7; num3 = 4; result1 = average (num1, num2); result2 = average (num3, num1); } double average (int x, int y) { return ( (x+y) / 2); } 57 x y

107 Esimerkki (pääohjelma vaipuu uneen) xy num1 num2num3 result1result2 5 74 ?? 57 // Funktio on nyt aktiivinen #include void main ( ) { int num1, num2, num3; double result1, result2; num1 = 5; num2 = 7; num3 = 4; result1 = average (num1, num2); result2 = average (num3, num1); } double average (int x, int y) { return ( (x+y) / 2); }

108 Esimerkki (funktio hoitaa hommaansa) xy num1 num2num3 result1result2 5 74 ?? 57 // 5 + 7 is 12; 12 / 2 == 6 #include void main ( ) { int num1, num2, num3; double result1, result2; num1 = 5; num2 = 7; num3 = 4; result1 = average (num1, num2); result2 = average (num3, num1); } double average (int x, int y) { return ( (x+y) / 2); }

109 Esimerkki (Funktio on valmis ja palauttaa tuloksen) xy num1 num2num3 result1result2 5 74 ?? 57 // 5 + 7 is 12; 12 / 2 == 6 6 #include void main ( ) { int num1, num2, num3; double result1, result2; num1 = 5; num2 = 7; num3 = 4; result1 = average (num1, num2); result2 = average (num3, num1); } double average (int x, int y) { return ( (x+y) / 2); }

110 Esimerkki (pääohjelma herää; funktio nukkuu) x y num1 num2num3 result1result2 5 74 6? funktio nukkuu #include void main ( ) { int num1, num2, num3; double result1, result2; num1 = 5; num2 = 7; num3 = 4; result1 = average (num1, num2); result2 = average (num3, num1); } double average (int x, int y) { return ( (x+y) / 2); }

111 Sääntöjä funktioista  Et voi määritellä uutta funktiota toisen funktion sisältä  Funktiot ovat yleensä luokan sisällä (tästä puhutaan seuraavalla luennolla)  Sijoita funktioiden toteutus kutsuvan koodin yläpuolelle  Muussa tapauksessa esittele funktio ennen käyttöä funktion prototyypillä  Funktiot eivät näe toistensa muuttujia

112 Funktion prototyyppi (jos varsinainen toteutus on vasta myöhemmin) #include void printNum (int myNum); void main ( ) { int num1, num2, num3; num1 = 5; num2 = 7; num3 = 4; printNum (num1); } void printNum (int myNum) { // Remember, printNum can’t see num1, num2 or num3! cout << myNum << endl; } Here’s the prototype

113 Työlään tuntuinen funktiokutsu? -Esimerkki void main() { //Tama ohjelma piirtaa tikkataulun piirraYmpyra(10,150,200); piirraYmpyra(20,150,200); piirraYmpyra(30,150,200);... } void piirraYmpyra(int sade, int x, int y) {...; } Ärsyttääkö mikään???

114 Oletusparametrien käyttö funktiossa (default parameters) void main() { //Tama ohjelma piirtaa tikkataulun piirraYmpyra(10); piirraYmpyra(20); piirraYmpyra(30);... } void piirraYmpyra(int sade, int x = 150, int y = 200) {...; }

115 Oletusparametrit sääntöjä  Funktion parametreille voi antaa oletusarvot  Oletusarvot annetaan funktion toteutuksen esittelyssä.  Funktion määrittely on edelleen samanlainen void piirraYmpyra (int sade, int x, int y); void piirraYmpyra(int sade, int x = 150, int y = 200) {  Funktiota kutsuttaessa parametrin arvon voi jättää kirjoittamatta jos parametrille on määritelty oletusarvo  Funktio käyttää tällöin oletusarvoa  Jos parametrillä ei ole oletusarvoa, niin sitä edeltävillekkään parametreille ei voi antaa oletusarvoa   määritä pakolliset parametrit ensin ja oletusarvoiset parametrit viimeiseksi

116 Funktioiden uudelleenmäärittely (overloading) void main() { string omaString; int omaInt; tulosta(omaString); tulosta(omaInt); } void tulosta( string x) { cout << “tulostetaan string: “ << x << endl; } void tulosta( int x) { cout << “tulostetaan int: “ << x << endl; }

117 Funktioiden uudelleenmäärittely (overloading)  Saman nimisestä funktiosta voi tehdä eri versioita  Versiot eroavat toisistaan parametrien määrän tai tyyppien suhteen  Paluuarvot voivat olla samaa tai eri tyyppiä  Jos ainoana erona on paluutyyppi, saadaan käännösvirhe  Kääntäjä osaa valita oikean funktion kutsussa käytettyjen parametrien perusteella.  Käyttöesimerkki: keskiarvon laskeminen käyttäen erityyppisiä parametreja (sen sijaan että toteuttaisimme IntKeskiarvo(), DoubleKeskiarvo(),…

118 Avoimet funktiot (Inline function)  Funktioon hyppäämiseen ja sieltä palaamiseen kuluu vähän aikaa  Jos funktio on hyvin pieni (2-3) riviä funktio ei ole kovin “kustannustehokas”  Jos funktion esittelyyn liittää varatun sanan inline, kääntäjä kopioi funktion koko sisällön niihin kohtiin missä funktiota kutsutaan  Avoimen funktion kääntöpuoli: koska funktio kopioidaan jokaiseen kutsukohtaan, ohjelmatiedoston koko kasvaa.

119 Avoin Funktio (inline function) Esimerkki inline int Tuplaa(int) void main() { int luku; cout << “syötä luku: “<< endl; cin >> luku; luku=Tuplaa(luku)...; osta(omaInt); } inline int Tuplaa( int x ) { return 2*x; }

120 Rekursiiviset Funktiot  Funktio voi kutsua itseään (rekursio)  Käytetään yleensä tilanteissa, missä funktion saamia tietoja käsitellään samalla tavalla kuin funktion tuottamaa tulostakin.  Pidä huolta, että rekursio loppuu jossain pisteessä!

121 Yhteenveto- Funktiot  Funktio on looginen ryhmä komentoja  Uudelleenkäytettävää koodia  Kirjoita kerran  Kutsu niin monta kertaa kuin haluat  Edut  Uudelleenkäytettävää  Helpottaa abstraktiota  Vähentää monimutkaisuutta  Vähentää koodin pituutta

122 Yhteenveto- Funktiot  Fuktiot noudattavat seuraavanlaista mallia:  Funktioille voi määritellä oletusparametereja. Kaikkia parametreja ei näin tarvitse täyttää funktiota kutsuttaessa unsigned short int LaskeAla (int pituus, int leveys) Paluuarvon tyyppiFunktion nimiParametrien nimet Parametrien tyypit

123 Yhteenveto- Funktiot  Saman nimisestä funktiosta voi tehdä eri versioita (overloading)  Funktioille voi määritellä oletusparametereja. Kaikkia parametreja ei näin tarvitse täyttää funktiota kutsuttaessa  Ohjelmasta saadaan tehokkaampi määrittelemällä lyhyet funktiot ”inline”-funktioiksi  Funktio voi kutsua itseään (rekursiivinen funktio)


Lataa ppt "Olio-ohjelmoinnin perusteet luento 3: Muuttujista ja funktioista Sami Jantunen LTY/Tietotekniikan osasto."

Samankaltaiset esitykset


Iklan oleh Google