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 yksinkertaisita 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 = 32767; cout<< myInt << endl; myInt++; cout<< myInt << endl; myInt++; cout<<myInt<<endl; Tuloste: 32767 0 1

19 Etumerkillisten kokonaislukujen ylivuoto Suurinta positiivista arvoa seuraa suurin negatiivinen arvo Esim: int myInt = 32767; cout<< myInt << endl; myInt++; cout<< myInt << endl; myInt++; cout<<myInt<<endl; 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 #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; … }

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 muuttjien käyttöön ei yleensä ole tarvetta Malta vielä hetki. Seuraavilla luennoilla tulee lisää (=oliopohjaisia) ratkaisuja muuttjien 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 sunktion 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ä!


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

Samankaltaiset esitykset


Iklan oleh Google