Upload
ruslan-bulko
View
1.006
Download
22
Embed Size (px)
Citation preview
- 1 -
Vilniaus kolegija
Elektronikos ir informatikos fakultetas
Programavimas C kalba
Parengė: lekt. Mindaugas Liogys
- 2 -
- 3 -
1. I-OJI PASKAITA: Pradinės sąvokos
1.1. C programavimo kalbos ypatybės
• Gerai išvystyta aukšto lygio programavimo kalba.
• Dauguma (vos ne visos) komercinės programos parašytos būtent C arba C++ kalba. De facto tai pramoninių
programų rašymo standartas
• Kada atsirado C kalba? 1969 metais išleista C kalbos pirma versija. Sukurta kaip sudėtinė UNIX operacinės sistemos
dalis. Vėlesnės UNIX versijos parašytos C kalba
• Kodėl pavadinta C? Buvo dvi ankstesnės versijos – A ir B, bet jos buvo nevykusios. Iš trečio karto pasisekė sukurti
pasisekusią programavimo kalbą. Todėl ir pavadinimas C.
• C – tai nepriklausoma nuo mašinos tipo procedūrinė programavimo kalba
• Koks skirtumas tarp C ir C++? C++ – tai objektinis procedūrinės programavimo kalbos C poaibis
• Kokias programas galima parašyti naudojant C/C++?
1.2. Programavimo kalbų klasifikacija
• Kas yra kalba ? Ar pvz. matematika kalba ?
• Kokios kalbos yra natūralios ir kokios dirbtinės ?
• Kodėl programos nerašomos natūralia kalba ? Kas nulėmė tokią programavimo kalbų struktūrą kokia yra dabar ?
• Ţemo lygio programavimo kalbos:
• - mašininiai kodai
• - asembleriai (ypatybės ?)
• Aukšto lygio programavimo kalbos:
• -procedūrinės: Fortran, Pascal, C
• -interpretatoriai: Basic
• objektinės: C++, Java, Delphi (ypatybės ?)
1.3. Programavimo priemonių rinkinys
• Ką reikia turėti norint parašyti veikiančią programą ?
Trys pagrindiniai komponentai:
Redaktorius – tam, kad parašyti programos išeities kodą. Tinka bet koks redaktorius (pvz. Notepad), bet daţniausiai
naudojami specializuoti
Kompiliatorius – tam, kad parašytą programos kodą paversti į mašininius kodus, taip vadinamą objektinį modulį
Linkeris – kad prie objektinio modulio prijungti kitus objektinius modulius, bibliotekose surinktą kodą ir gauti
vykdomąją programą.
• Šiuolaikinėse sistemose daţniausiai naudojamos integruotos sistemos, apjungiančios visus tris komponentus
• Vis daţniau naudojamas vizualinis programavimas. Tačiau sunkiausią dalį programuotojas vis tiek turi padaryti pats
1.4. C kalbos programavimo priemonės
Yra sukurta daug programavimo priemonių rašyti programas C kalba:
Nemokamos: gcc kompiliatorius, Mars C, lcc ir t.t.
Komercinės: Microsoft C, Borland C, Symantec C ir t.t.
Įprastinės: Turbo C, Borland C, Symantec C
Vizualinio programavimo: Microsoft Visual C, Borland C Builder
• Kurios geriausios ? Tos, prie kurių labiausiai esi pripratęs ir gali greičiausiai pasiekti reikiamą tikslą.
1.5. Programos leksika ir sintaksė
• Programą, parašytą C kalba, sudaro vienas arba keli failai
• Kiekvieną failą sudaro funkcijų ir kintamųjų bei veiksmų su jais aprašymas
• Leksiškai programa susideda iš bazinių arba raktinių ţodţių (angl. keywords) specialių ţenklų, identifikatorių,
konstantų ir komentarų
• C kalboje naudojamos tiek didţiosios, tiek maţosios raidės, tačiau yra priimta eilė neoficialių susitarimų, kurių
pravartu laikytis
- 4 -
1.6. Kintamieji ir konstantos
• Duomenys, kuriuos apdoroja kompiuteris – tai konstantos ir kintamieji
• C kalboje visi kintamieji turi būti aiškiai aprašyti
• Kintamojo aprašymas – tai jo tipo specifikavimas, kintamojo vardo uţdavimas, eilė neprivalomų modifikatorių, kurie
apibrėţia vidines kintamojo vaizdavimo ypatybes arba jo saugojimo klasę.
• Aprašant kintamąjį galima nurodyti jo pradinę reikšmę
• Identifikatorius – tai seka raidţių arba skaičių, kurie prasideda raide arba pabraukimo ţenklu _
• Kintamojo vardas – tai identifikatorius . Vardas gali būti bet kokio ilgio, bet tik pirmi 32 simboliai bus reikšminiai
1.7. Kintamųjų tipai
• Tipas – bet kuriai programavimo kalbai fundamentali sąvoka
• C kalboje skiriami 4 kategorijų tipai
• Tuščias tipas (void)
• Skaliarinis tipas:
– aritmetiniai tipai
– išvardijimai (enumerations)
– rodikliai (pointers)
– nuorodos (references)
• Tipas funkcija
• Agreguotas tipas:
– masyvai (arrays)
– struktūros ir sąjungos
– klasės (tik C++ poaibyje)
• Kitaip kintamieji gali būti skirstomi į :
– pagrindinius
– išvestinius
• Pagrindiniai tipai C kalboje yra:
void char int float double
ir jų variantai su raktiniais žodžiais
short long signed unsigned
• Išvestiniai tipai:
– rodikliai ir nuorodos į kitus duomenų tipus
– funkcijos
– struktūros ir sąjungos
– klasės
1.8. Duomenų tipas int
• C kalboje duomenų tipas int (integer, integral ?) savyje apjungia duomenų tipus char, short, long
• Pats duomenų tipas int yra bazinis ir priklauso nuo operacinės sistemos skiltiškumo: jo dydis lygus operacinės
sistemos adresavimo skiltiškumui (16 bitų Win16, 32 bitai Win32, ateityje 64 bitai ?)
•
bitai min max
Char 8 -128 127
unsigned char 8 0 255
short int 16 -32768 32767
Int 16 -32768 32767
unsigned int 16 0 65535
Long 32 -2147413648 2147483647
unsigned long 32 0 4294967295
- 5 -
1.9. Sveiko tipo konstantos
• Sveiko tipo (int) konstantos gali būti uţrašomos dešimtaine, aštuntaine arba šešioliktaine sistema:
• 15,5,65,156,253
• 015,05,065,0156,0253
• 0x15,0x5,0x65,0x156,0x25af
• Dešimtainės konstantos paprastoje formoje suprantamos kaip short int kintamieji.
• Jeigu reikia uţrašyti didesnius kintamuosius, reikia naudoti tipo modifikatorius U(u) – unsigned arba L(l) – long
• Pvz.: negalima rašyti 40156 , 1256789, -35674 ir t.t.
• Reikia rašyti 40156u, 1256789l, -35674l ir t.t.
• Galima naudoti tiek U ir L, tiek u ir l
1.10. Slankaus kablelio konstantos
• Sveikais tipais negalima aprašyti trupmeninių skaičių: reikia naudoti slankaus kablelio tipus
• C kalboje naudojami trys slankaus kablelio duomenų tipai: float, double, long double
bitai min max tikslumas
float 32 3,4*10-38 3.4*1038 7
double 64 1.7*10-308 1.7*10308 15
long double 80 3.4*10-4932 3.4*104932 19
1.11. Kintamųjų aprašymo pavyzdžiai
Keli kintamųjų aprašymo pavyzdžiai:
• int i;
• signed short a; unsigned short k;
• short a, b=125, abcd=0xabcd;
• unsigned char b1=156;
• float bb, c=0.0,z=1.e-5;
• double pi=3.1415928;
• long double c1; double _z1erw=123.4561;
1.12. Priėjimo modifikatoriai const ir volatile
• Neprivalomi modifikatoriai padeda apibrėţti kintamuosius, kurie programos vykdymo metu gali arba negali pakeisti
savo reikšmes
• const – negali
• volatile – gali (naudojamas pagal nutylėjimą)
•
• Pvz: const int max_const=32767;
• Naudojama, kuomet norima apsidrausti, kad programos vykdymo metu netyčia nebūtų pakeista reikalinga kintamojo
reikšmė
1.13. Komentarai
• C/ C++ kalboje komentarus galima įterpti dviem būdais:
1) /* ..... */ - komentarai rašomi tarp slešo ir ţvaigţdės ir ţvaigţdės ir slešo. Komentarų kiekis neribojamas
2) // po dviejų slešų visa eilutė iki galo traktuojama kaip komentarai
Pvz. /* aš čia dabar rašau komentarus
kiek noriu tiek rašau. ir čia komentaras int a/c;
dabar baigiu */
int a; // o dabar komentarai , int a – čia irgi komentaras
- 6 -
1.14. Gero stiliaus reikalavimai
Naudoti prasmingus pavadinimus globaliniams kintamiesiems ir trumpus pavadinimus – lokaliniams
Patartina globalinių kintamųjų aprašymus paydėti trumpu komentaru
• Pvz: int npending=0; // ivedimo srauto einamas ilgis
• Pvz: numberOfPoints – globalinis kintamasis
• nPoints – tiek globalinis, tiek lokalinis
• n –lokalinis kintamasis
Egzistuoja daug susitarimų ir vietinių tradicijų kaip reikėtų parinkti pavadinimus kintamiesiems įvairiose sistemose ir
uţdaviniuose
Būkite nuoseklūs: panašios kilmės objektams reikėtų parinkti atitinkamus pavadinimus, parodančius jų
panašumus ir skirtumus
Būkite tikslūs: vardas reiškia ne tik objektą, bet ir jo prasmę !!!
1.15. Operacijos ir išraiškos
Išraiška – tai seka operandų, operacijų ir skiriamųjų ţenklų.
Skiriamieji simboliai: [ ] { } , ; : ... * = #
Išraiškas kompiliatorius interpretuoja grieţtai besilaikydamas vyresniškumo taisyklių
Vyresniškumo taisyklės kaip matematikoje priimtos
1.16. Pagrindinės operacijos: aritmetinės operacijos
Tipinės operacijos:
• Sumavimo operacija +
• Atėmimo operacija –
• Daugybos operacija *
• Dalybos operacija /
C kalbos specifinės operacijos:
• Liekanos nustatymo operacija %
• Inkremento operacija ++
• Dekremento operacija --
1.17. Pagrindinės operacijos: poskiltinės loginės operacijos
• Vadinamos poskiltinėmis todėl, kad loginiai veiksmai atliekami su kiekviena kintamojo arba konstantos skiltimi atskirai
• Yra keturios:
• & - loginis IR (AND operacija)
• ^ - poskiltinis sumavimas moduliu 2 (XOR operacija)
• | - loginis ARBA (OR operacija)
• ~ poskiltinė inversija
1 operandas 2 operandas AND OR XOR
0 0 0 0 0
1 0 0 1 1
0 1 0 1 1
1 1 1 1 0
1.18. Pagrindinės operacijos: postūmio operacijos
• Poskiltinio postūmio operacijos:
>> - postūmis dešinėn
<< - postūmis kairėn
Pvz: half = addr>>1;
double = addr<<1;
Pvz: 5 - > 101 <<1 -> 1010 -> 10
12 -> 1100>>1 -> 110 -> 6
3 -> 11 <<3 -> 11000 -> 24
Priskyrimo operacija =. Pvz. a=b;
- 7 -
1.19. Pagrindinės operacijos: loginės operacijos ir santykio operacijos
Santykio operacijos:
• > - daugiau
• < - maţiau
• == - tapačiai lygu
• >= - daugiau arba lygu
• <= - maţiau arba lygu
• != - nelygu
• Teisinga, jei operandas iš kairės tenkina sąlygą
Loginės operacijos:
• && - loginis IR
• || - loginis ARBA
• ! – loginis NE
• Šios operacijos naudojamos sudarant logines išraiškas, turinčias tik dvi reikšmes: 1, jei išraiška teisinga ir 0, jei
išraiška neteisinga
1.20. Sąlygos operacija ? :
• Sąlygos operacija ? : apibrėţiama kaip:
cond ? TRUE_statement : FALSE_statement
• Pvz:
int a=4, b=3;
int i;
i=a>b ? a: b;
1.21. Duomenų tipų nurodymo operacija (kastingavimas)
• C kalboje duomenų tipus (operandų tipus) galima nurodyti aiškiai
• Toje operacijoje kintamasis bus traktuojamas kaip nurodyta privedimo operacijoje
• C++ operuojant su skirtingo tipo operandais duomenų tipus būtina kastinguoti
• Kintamasis įgis nurodytą tipą tik duotoje operacijoje;
pvz.: int a=8,b=5;
float var1,var2;
var1=a/b; // var1=1.0;
var2=(float)a/b; // var2=1.6;
var2=(float)a/(float)b; // var2=1.6;
1.22. Minimali programa C kalba
• int main() { }
• int main()
{
}
• int main(
)
{}
Pastabos: failo pavadinimas baigiasi plėtiniu .c arba .cpp
Tarpus kompiliatorius ignoruoja
Orientuotas į leksemas, o ne pozicijas faile
1.23. Pirmos programos pavyzdys
#include <stdio.h>
int main() {
unsigned int count;
count=1;
printf(―Hello world !!! \n count=%d‖,count ); }
Rezultatas: Hello world !!! count=1
- 8 -
2. II-OJI PASKAITA
2.1. Bazinio įvedimo/ išvedimo metodai
• Skirtingi metodai naudojami konsoliniame įvedime/išvedime ir programose su langais
• Čia kalbame apie konsolinį I/O
• C++ turi savus operatorius, C specialių operatorių neturi
• Naudojamos bibliotekinės funkcijos
• Kas yra biblioteka, kas yra funkcija ?
• Standartinė įvedimo / išvedimo biblioteka:
• #include <stdio.h>
• Konsolinio įvedimo / išvedimo biblioteka
• # include <conio.h>
2.2. Formatuotas išvedimas
• Pavadinimas: printf()
• Sinopsė:
• #include <stdio.h>
• int printf(const format[,arg]…)
• Aprašymas: į standartinį išvedimo įrenginį (koks įrenginys yra standartinis ?) išveda nurodytą duomenų srautą,
sutinkamai su nurodytu formatu format: tai gali būti literaliniai simboliai ir transformavimo specifikatoriai:
• %d – dešimtainis sveikas skaičius
• %f - slankaus kablelio skaičius
• %o - aštuntainis skaičius be ţenklo
• %x - šešioliktainis skaičius be ţenklo
2.3. Formatuotas išvedimas
• Pavadinimas: printf()
• Sinopsė:
• #include <stdio.h>
• int printf(const format[,arg]…)
• Aprašymas: į standartinį išvedimo įrenginį (koks įrenginys yra standartinis ?) išveda nurodytą duomenų srautą,
sutinkamai su nurodytu formatu format: tai gali būti literaliniai simboliai ir transformavimo specifikatoriai:
• %d – dešimtainis sveikas skaičius
• %f - slankaus kablelio skaičius
• %o - aštuntainis skaičius be ţenklo
• %x - šešioliktainis skaičius be ţenklo
Pavyzdţiai:
• printf(―iveskite varda:‖);
• printf(―turimas akciju skaicius %d\n‖,num);
• printf(―\n sio menesio %2d diena nupirkta %3d akciju\n‖,diena, num);
• printf(―vieneto kaina %f‖, kaina);
• printf(―vieneto kaina %4.2f bendra kaina %6.2f\n‖,kaina, total);
• printf(―vienetu skaicius %d vieneto kaina %4.2d bendra kaina %6.2f\n‖,kiekis, kaina, total);
• printf(― | vienetu skaicius %d vieneto kaina %4.2d bendra kaina %6.2f | \n‖,kiekis, kaina, total);
2.4. Konsolinis įvedimas
• Pavadinimas: scanf()
• Sinopsė:
• #include <stdio.h>
• int scanf(const format[,arg]…)
• Aprašymas: iš standartinio įvedimo įrenginio (koks įrenginys yra standartinis ?) nuskaito nurodytą duomenų
srautą, sutinkamai su nurodytu formatu format:
• %d – dešimtainis sveikas skaičius
• %f - slankaus kablelio skaičius
• %o - aštuntainis skaičius be ţenklo
- 9 -
• %x - šešioliktainis skaičius be ţenklo
• %c - simbolis
• scanf(―%d‖, &num);
• scanf(―%f‖,&price);
• scanf(―%d %d‖,&diena, &num);
• scanf(―%4.2f %6.2f‖,&kaina, &total);
• scanf(―%d %f %f‖,&kiekis, &kaina, &total);
• scanf(―%c‖,&raide);
• scanf(―%d‖,a[i]);
• scanf(―%s‖ pavadin);
2.5. Simbolinis įvedimas/ išvedimas
• Pavadinimas: getchar – įvedimo funkcija
• Sinopsė:
• #include <stdio.h>
• int getchar(void)
Aprašymas: nuskaito ir graţina eilinį simbolį iš standartinio nuskaitymo įrenginio
Diagnostika: graţina EOF simbolį, jeigu pasiektas failo galas arba klaidos simbolį, įvykus įvedimo klaidai. Visais kitais
atvejais graţina nuskaitytą simbolį
• Pavadinimas: putchar – išvedimo funkcija
• Sinopsė:
• #include <stdio.h>
• int putchar(int c)
Aprašymas: išveda eilinį simbolį į standartinį išvedimo įrenginį
Diagnostika: graţina išvestą simbolį, jeigu išvedimas atliktas sėkmingai, priešingu atveju graţina EOF simbolį.
• Pavyzdţiai
#include <stdio.h>
int main() {
int c;
c=getchar(); /* nuskaito simboli ir priskiria ji kintamajam c */
putchar(c);
putchar(‗m‘);
putchar(‗\t‘);
putchar(‗\007‘);
}
2.6. Skaičiavimo proceso valdymo operatoriai
• Skaičiavimo procesas – tai programos operatorių vykdymo seka
• Paprastai programos operatoriai vykdomi elės tvarka: iš viršaus į apačią, iš kairės į dešinę
• Kartais normalią veiksmų vykdymo seką reikia pakeisti
• Kam reikia pakeisti valdymo seką ? Uţdavinio sprendimas reikalauja
• Naudojami specialūs operatoriai – skaičiavimo proceso valdymo operatoriai
• Šakojimosi operatoriai, sąlygos operatoriai, ciklo operatoriai
• if, if-else, while, do while, switch – case - break, for, break, continue, goto, return
2.7. Operatorius if
• Šakojimosi operatoriai išrenka programoje galimą proceso tąsą iš eilės alternatyvų
• C kalboje yra du šakojimosi operatoriai – if ir switch
• if – paprasčiausias sąlygos operatorius
• if operatoriaus apibrėţimas:
if(cond_expression) TRUE_statement;
• cond_expession – sąlygos išraiška
• TRUE_statement – išraiška, kuri vykdoma jei sąlyga teisinga
- 10 -
• Pavyzdţiai:
• if(x>largest) largest=x;
• if( val<min || val>max) {
printf(“reiksme %d iseina uz leidziamo diapazono ribu\n”,val);
printf(“leidziamas diapazonas %d %d\n”, min, max); }
• if(num<0) num=-num;
• c=getchar();
if(c==„\n‟) lines+=1;
• ekvivalentinis if((c=getchar()) ==„\n‟) lines+=1;
• neekvivaletinis if(c=getchar()==„\n‟) lines+=1;
2.8. Operatorius if-else
if ir else – raktiniai žodžiai
• Jeigu išraiška teisinga – vykdoma pirmoji išraiška, jeigu išraiška neteisinga – pirmoji išraiška praleidţiama ir
vykdoma antroji išraiška
• Tiek pirmoji, tiek antroji išraiškos gali būti paprastos arba blokinės
• Apibrėţimas:
• if(cond_expression) True_statement;
else False_statement;
• cond_expession – sąlygos išraiška
• TRUE_statement – išraiška, kuri vykdoma jei sąlyga teisinga
• FALSE_statement – išraiška, kuri vykdoma, jei sąlyga neteisinga
Pavyzdžiai
• if(kiekis<100) printf(―atsargu kiekis mazas‖);
else printf(―atsargu kiekis pakankamas‖);
• if(x!=0)
y=y/x;
else {
printf(―klaida: dalyba is nulio‖);
y=0; }
• if(hour>=3 && hour<17)
rate*=1.02;
else if(hour>=17 && hour<23)
rate*=1.01;
else rate*=0.99;
Pastaba: operatoriai if-else yra inkliuzyviniai: į vieną if-else operatorių galima įstatyti kitą.
Inkliuzyvų kiekis neribojamas
2.9. Else atskyrimo problemos: klaidų šaltinis
• if(c>‘ ‗)
if(c>=‗0‘ && c<=‗9‘)
digits+=1;
else
count+=1;
• Kuriam if priskiriamas else ?
• if(c>‘ ‗)
{
if(c>=‗0‘ && c<=‗9‘)
digits+=1;
}
else
count+=1;
- 11 -
2.10. Operatoriai switch-case- break
• Daţnai pasitaikantis programavimo uţdavinys: išrinkti vieną alternatyvą iš daugelio galimų variantų
• Tai patogu atlikti naudojant switch-case operatorių
• Operatoriaus sintaksė:
switch(switch_expression) {
case const1: statement1; [break;]
case const2: statement2; [break;]
....
case constn: statementn; [break;]
[default: statemnt n+1;]
}
• Iliustracija
• input=getchar();
switch(input) {
case „d‟: z=z*5;
case „a‟:
case „A‟: z-=1; break;
case „p‟:
case „P‟: z+=1; break;
default: printf(“neteisingas pasirinkimas\n”);
break;
}
2.11. Ciklo operatoriai
• Ciklas – tai tų pačių veiksmų pakartojimas nurodytą skaičių kartų
• C kalba turi tris ciklo uţrašymo formas:
• while(cond_expression) operators;
• do operators while(cond_expression);
• for(init_expr; cond_expr; incr_expr) operators;
• Kuri uţrašymo forma geriausia ? Priklauso nuo konkrečių sąlygų
• Visomis uţrašymo formomis galima uţrašyti bet kurį ciklą
2.12. Besąlyginio perėjimo operatoriai
• Yra keturi besąlyginio perėjimo operatoriai C kalboje: break, continue, goto, return
• Besąlyginio perėjimo operatoriai – sutikus tokį operatorių programos valdymas automatiškai perduodamas į apibrėţtą
vietą
• break – išeina iš duoto bloko ir tęsia vykdymą iš karto nuo sekančio operatoriaus uţ bloko
• continue – naudojamas cikle, automatiškai pereina prie sekančios ciklo iteracijos
• return – grįţta iš funkcijos. valdymas perduodamas į funkcijos iškvietimo tašką
• goto label – valdymas automatiškai perduodamas į programos tašką, kurį ţymi markeris label
2.13. Gero stiliaus reikalavimai
1) Formatuokite kodą taip, kad pabrėžti jo struktūrą
• Blogas formatavimas:
for(n++;n<100;n++) field[n++]=„\0‟;
*i=„\0‟; return(„\n‟);
• Geras formatavimas:
for(n++;n<100;n++)
field[n++]=„\0‟;
*i=„\0‟;
return „\n‟;
2) rašykite išraiškas natūralia forma: rašykite taip, kaip skaitytumėte
Blogai:
if(!(block_id<actblk) || !(block_id>=unblock))
- 12 -
Gerai:
if((block_id>=actblk) || (block_id<unblock))
3) Naudokite skliaustelius, kad panaikinti neaIŠKUMUS
• Pavyzdys su kelintiniais metais
• leap_year= y%4==0 && y%100!=0 ||y%400==0;
• leap_year= ((y%4==0) && (y%100!=0)) ||(y%400==0);
4) Skaidykite sudėtingas išraiškas:
• Blogai: x+=(xp=(2*k<(n-m) ? c[k+1] : d[k--]));
• Gerai: if(2*k<n-m)
xp=c[k+1];
else
xp=d[k--];
x+=xp;
5) Būkite paprastesni: nereikia persistengti rašant labai įmantrias išraiškas:
• Blogai: subkey=subkey>>(bitoff>>3)<<3));
• Gerai: subkey=subkey>>(bitoff&0x7));
subkey>>=bitoff&0x7;
Absoliučiai mįslinga konstrukcija:
child=(!LC&&!RC)?0:!(LC?RC:LC);
Paprastai: if(LC==0 && RC==0)
child=0;
else if(LC==0)
child=RC;
else
child=LC;
6) Būkite atsargūs u pašaliniais efektais: ++ tipo operacijos turi pašalinius efektus: jos ne tik gražina reikšmę, bet ir
pakeičia operando reikšmę
• Blogai: str[i++]=str[i++]=„A‟;
(blogai, nes nėra apibrėţta ++ operacijos vykdymo tvarka)
• Gerai: str[i++]=„A‟;
• str[i++]=„A‟;
3. III-IOJI PASKAITA: Rodyklės. Nuorodos. Masyvai.
3.1. Rodyklės sąvoka
• Idėja panaudoti adresus – labai sena.
• Programose rašomose mašininiais kodais arba asembleriu daţnai sutinkamas uţdavinys – perkelti duomenis iš vieno
adreso į kitą
• Tokiose programose tai atliekama paprastai
• Aukšto lygio programavimo kalbose daţnai apie adresus negalvojama, o kai kuriose ir nėra jokių priemonių operuoti su
adresais
• C kalboje yra abiejų būdų komponentų: tiek darbui su adresais, tiek ir ne tiesioginio adresavimo galimybių
• Kas yra rodiklis? Kintamasis, kuriame saugomas adresas
• Apibrėţimas: kntamasis-rodiklis (arba tiesiog vadinama rodikliu) – ta kintamasis, skirtas adreso saugojimui atmintyje
• Kam naudojami rodikliai?
Kad efektyviai prieiti prie duomenų
Lanksčios programos parašymui
Kintamųjų, perduodamų į funkciją, reikšmių pakeitimui
Darbui su dinamiškai paskirstoma atmintimi
Priėjimui prie aparatinių kompiuterio resursų arba papildomų įrenginių
3.2. Rodyklės ir nuorodos C kalboje
• C kalboje apibrėţtos dvi specialios operacijos kintamųjų adresavimui per rodiklius
o operacija & - nuorodos (reference) operacija
o operacija * - rodiklio (pointer) operacija
Operacijos & rezultatas – tai objekto, kuriam taikoma i operacija, adresas
Operacija * – tai kreipinys į atminties ląstelę, kurios adresas saugomas tame objekte
- 13 -
3.3. Rodiyklės aprašymas, inicializavimas ir naudojimas
• Kintamasis p aprašomas kaip rodiklis į sveiko tipo kintamąjį. Tai reiškia kad jame bus saugomas sveiko tipo kintamojo adresas
• int num1=3, num2=6, *p;
• Visi rodikliai turi būti inicializuoti
• p=&num1;
• Rodiklis p dabar ―rodys‖ į kintamąjį num1
• Dabar kintamąjį num1 bus galima adresuoti tiesiogiai, naudojant priskyrimo operaciją = , arba kaip rodiklį, naudojant operatorių *
3.4. Dar apie rodykles
• Svarbu prisiminti, kad ţenklas * - tai operatorius
• Kuomet jis naudojamas su dviem operandais (binarinis operatorius) – tai daugybos operatorius
• Kuomet jis naudojamas kaip unarinis operatorius – laikoma, kad tai rodiklis, nurodantis į ţenklą
• Negalima sumaišyti
• Sudėtingose išraiškose kartais gali būti painu suprasti kaip reikia perskaityti
• Pvz: p= a* *b *c **d;
3.5. Pratimai su rodyklėmis: operacijos & ir *
• Kas neteisinga ţemiau pateiktoje programoje
#iclude <stdio.h>
int main() }{
int num1=100, *p;
printf(“num1 is %d\n”,num1);
printf(“*p is %d\n”,*p);
}
Ar tokia programa bus sukompiliuota?
Ar bus įvykdyta ?
Kas bus išspausdinta ?
#include <stdio.h>
main() }{
int num1=3, num2=6;
int *p;
p=&num1;
printf(―%d\n‖,*p);
*p=20;
printf(―%d‖,*p);
printf(―%d\n‖,num1);
p=&num2;
printf(―%d\n‖,*p);
}
• 510 p ?
• 504 num2 6
• 500 num1 3
• 510 p 500
• 504 num2 6
• 500 num1 3
• 510 p 500
• 504 num2 6
• 500 num1 20
• 510 p 504
• 504 num2 6
• 500 num1 20
• Kas bus išvesta atlikus šitą programą
#include <stdio.h>
int main(){
int count=10,x;
int *ip;
ip=&count;
x=*ip;
printf(―count=%d, x=%d\n‖,count,x); }
• Kas bus išvesta atlikus šitą programą
#include <stdio.h>
int main(){
int i1,i2;
int *p,*q;
i1=5;
p=&i1;
i2=*q/2+10;
q=p;
printf(―i1=%d i2=%d‖,i1,i2);
printf(―*p=%d *q=%n‖,*p,*q); }
- 14 -
3.6. Rodyklių aritmetika
• Svarbu aprašant rodiklius su teisingu duomenų tipu
• Naudojama padidinant / sumaţinant rodiklio reikšmę, palyginant rodiklius tarpusavyje, atimant vieną rodiklį iš kito ir
pan
• char *p;
• *p – tai simbolis
• p++ ekvivalentiška p=p+1
• short *p;
• *p – tai trumpas sveikas skaičius
• p++ - ekvivalentiška p=p+2
3.7. Masyvai
• Masyvas – tai eilės tvarka, vienas paskui kitą, atmintyje išdėstyti vieno ir to paties tipo elementai.
• Kiekvienas masyvas turi nuosavą unikalų pavadinimą
• Priėjimas prie masyvo elementų atliekamas naudojant masyvo pavadinimą ir elemento eilės numerį
• Pagrindinis masyvo privalumas: leidţia saugoti aibę elementų, aiškiai nenurodant kiekvieno iš elementų pavadinimų
• Masyvai gali būti vienmačiai arba daugiamačiai (dvimačiai, trimačiai, ir t.t. Kada pabaiga ?)
• Masyvo poţymis – kvadratinių skliaustelių buvimas šalia pavadinimo: char buffer[256]; int a[3]; ir t.t.
• Pagrindinės masyvų savybės C kalboje:
1) Visi masyvo elementai yra vieno ir to paties tipo
2) Aprašant masyvą nurodomas jo elementų tipas, masyvo pavadinimas ir bendras masyvo elementų skaičius
3) Pirmo masyvo elemento indeksas visada yra lygus 0, paskutinio n-1
4) Masyvo elementai į atmintį surašomi nuosekliai (pvz. 3 elementas atmintyje visada bus po 2)
5) Dvimačiai masyvai surašomi eilutėmis: pirmiausia pirmos eilutės elementai, po to antros ir t.t.
• Masyvo vardas yra rodiklis-konstantė (tiesiog rodiklis) ir visada lygus masyvo pradžios adresui
3.8. Masyvo inicializavimo būdai
• C kalboje numatyti du masyvų inicializavimo būdai:
• Inicializavimas pagal nutylėjimą: taikomas tik statiniams arba išoriniams masyvams
• Tiesioginė elementų inicializacija:
• char array[10]={„a‟,‟b‟,‟c‟,‟d‟,‟e‟,‟f‟,‟g‟,‟h‟,‟i‟,‟j‟};
• char array[]={1,2,3,4,5};
• char array[5]; array[0]=1; array[1]=2; array[2]=3;
• char array[10]; int i; for(i=0;i<10;i++) array[i]=i;
3.9. Nuorodos į masyvo elementus
• Darbas su atskirais masyvo elementais arba indekso, arba operacijos * pagalba
• Abu darbo su masyvo elementais būdai yra lygiaverčiai
• Naudojant indeksus
• masyvo_vardas[sveikaskaitinė_išraiška]
• Short a[15]; a[0]=5; a[2]=7; a[5]=a[0]+a[2]; a[7]=a[5];
• Naudojant rodiklius:
• short a[15]; *a=5; *(a+2)=7; *(a+5)=*a+*(a+2); *(a+7)=*(a+5);
3.10. Daugiamačiai masyvai
• C kalba palaiko ir daugiamačius masyvus
• Masyvo matiškumas – tai indeksų, naudojamų adresuojant masyvo elementą, skaičius
• Daugiamačių masyvų elementai saugomi atmintyje dešiniausio indekso didėjimo tvarka
• Dvimačio masyvo vardas yra rodiklis į rodiklių masyvą. Masyvo elementai yra rodikliai į kiekvienos eilutės pradţios
elemento adresą
• Matiškumų skaičius nėra ribojamas, praktiškai niekada nenaudojami didesni nei trimačiai masyvai
- 15 -
3.11. Programos su masyvais pavyzdys
#include <stdio.h>
int main() {
const float investment=6000, interest=.085;
float rate; double value[5];
int year;
rate=1+interest; value[0]=investment; year=1;
while(year<5) {
value[year]=value[year-1]*rate;
year+=1; }
printf(―pradinis indelis %.2f\n‖,value[0]);
year=1;
while(year<5) {
printf(―metai %d: %.2f\n‖,year,value[year]);
year+=1; } }
3.12. Kelios pastabos apie masyvus
• C kalboje nėra atliekamas joks masyvų adresavimo korektiškumas
• Tai daţna klaidų prieţastis: adresuoti į tuos masyvų elementus, kurių pagal pradinį masyvo aprašymą paprasčiausiai
nėra
• Viena iš daugiamačių masyvų reto naudojimo prieţasčių – didesnis adresavimo sudėtingumas ir didesnė klaidų
tikimybė
short a[30]; for(i=0;i<30;i++) a[i]=i*i; //gerai
a[30]=a[29]; // blogai , tik 30 elementu galima
a[7*7]=a[5*5] // blogai
Visa atsakomybė uţ indeksų naudojimo teisingumą išimtinai priklauso programos autoriui
Jei adresuojama į masyvo elementą su didesniu indeksu, programa nulūţta
3.13. Masyvų kopijavimas
• Masyvai turi būti kopijuojami elementas po elemento
• Int prev[20],current[20];
• Neteisingai:
prev=current; // kompiliavimo klaida
• Teisingai:
i=0;
while(i<20) {
prev[i]=current[i];
i+=1; }
3.14. Inkremento ir dekremento operatoriai masyvuose
• Dėl savo patogumo ir efektyvumo inkremento/ dekremento operatoriai daţnai naudojami veiksmuose su masyvais
• Prefiksinė forma ++x, --x; postfiksinė forma x++,x—
• Prefiksinė forma – reikšmė pakeičiama nedelsiant, postfiksinė – reikšmė pakeičiama atlikus kitus veiksmus
• x=3; y=++x; // y? x?
• x=3; y=x++; // y? x?
• Pvz:
i=0; i=0;
while(<size) { => while(i<size) table[i++]=0;
table[i]=0;
i=i+1;
}
3.15. Simbolių masyvai
• Labai daţnai pasitaikantis uţdavinys: apdoroti tekstą (tekstinę informaciją)
• Tekstas kompiuterio poţiūriu – tai simbolių rinkinys. Bet mums reikia ne atskirų simbolių, o jų junginių
- 16 -
• Kiekvienas teksto simbolis kaip ir masyvo elementas, nes visi elementai vienodo tipo
• Bet tokie masyvai specifiniai – tarpai, taškai, ir t.t. Kur pabaiga masyvo ?
• Daţnai iš anksto neţinomas teksto ilgis, daţnai būna kintamas
• Kitose kalbose (naujesnėse) simbolių sekų saugojimui yra skirti specialūs duomenų tipai, C to neturi
• Tokie duomenų tipai vadinami simbolių eilutėmis arba stringais
• Uţrašymas: simbolių eilutė – tai seka bet kokių simbolių, įtrauktų į dvigubas porines kabutes
• Simbolių eilutė C kalboje kompiuterio atmintyje saugoma kaip char tipo elementų masyvas, kurio pabaigoje yra
simbolis ‗\0‘ (null-terminatorius)
• Stringas – tai simbolių seka, pasibaigianti simboliu nul-terminatoriumi. Todėl jo ilgis visada vienetu didesnis negu
yra prasmingų simbolių !!!!!
• Daţnai pamirštama, kad stringui reikia išskirti vienetu daugiau, negu numatome saugoti simbolių. Aišku galima
rezervuoti ir dar daugiau atminties ląstelių.
3.16. Stringų aprašymai
• Aprašoma kaip ir char tipo duomenų masyvas:
• Ekvivalentiški aprašymai:
• char string[]=”eilute”;
• char string[7]=“eilute”;
• char string[7]={„e‟,‟i‟,‟l‟,‟u‟,‟t‟,‟e‟,‟\0‟};
• char *string=“eilute”;
Pastaba: svarbu suprasti, kad priskiriant eilutės reikšmę į rodiklio string atminties ląstelę persiunčiama ne visa eilutė, o tik
jos pirmojo elemento adresas. Todėl stringų, kaip ir kitų masyvų, negalima perkopijuoti prilyginant jų pavadinimus
3.17. Eilučių apdorojimas
• Veiksmams su simbolių eilutėmis yra parašyta daug standartinių funkcijų
• Reikia įtraukti header failą string.h, kad galėtume operuoti tomis funkcijomis
• #include <string.h>
• Keli pavyzdţiai:
• strcpy(src,dest) – perkopijuoti vieną eilutę į kitą
• strcmp(src,dest) – palyginti vieną eilutę su kita
• strcat(str1,str2) – prijungti vieną eilutę prie kitos
• Ir dar daug kitų funkcijų
Pavyzdys su stringais
#include <stdio.h>
int main() {
int i;
char first[11];
printf(“prasau ivesti varda:”);
i=0;
while(i<10 && (first[i]=getchar()) !=„\n‟)
i+=1;
first[i]=„\0‟;
printf(“ivestas vardas: %s\n”,first);
}
3.18. Masyvai kaip rodyklės
• Masyvų aprašymas su kvadratiniais skliausteliais paprastas ir patogus būdas tačiau turi trūkumų:
• Negalima rezervuoti didelių masyvų (didesnių negu 32676 elementai)
• Masyvo dydį reikia nurodyti iš karto ir negalima vėliau jo keisti (daţnai nelengva iškarto nustatyti kokio dydţio masyvo mums
reikės)
• Yra antras masyvų aprašymo būdas, neturintis tokių apribojimų – masyvų aprašymas per rodiklius:
• Pvz: short *a; long *b;
Tačiau kol kas nenurodytas masyvo dydis. Tą būtina padaryti !!!
- 17 -
3.19. Masyvų dydžio nurodymas
• Naudojamos standartinės funkcijos, aprašytos header faile alloc.h:
• #include <alloc.h>
• Atminties rezervavimui naudojamos funkcijos malloc() ir farmalloc(), atminties atlaisvinimui naudojamos funkcijos
free() ir farfree().
• Atmintį atlaisvinti būtina, kai ji nėra reikalinga
• Reikalingos atminties dydis šiose funkcijose visada uţduodamas baitais . Todėl reikia pačiam apskaičiuoti reikalingą
baitų skaičių arba pasinaudoti komanda sizeof()
Masyvų dydţio nurodymo pavyzdys
• Reikia 100 elementų trumpų skaičių masyvo ir 300 elementų slankaus kablelio skaičių masyvo
• Pvz:
#include <alloc.h>
int main() {
short *a;
float *b;
a=malloc(200); // nes kiekvienas short tipo elementas 2 baitai
a=malloc(100*sizeof(short)); // arba taip
b=farmalloc(1200); // nes kiekvienas float elementas 4 baitai
b=farmalloc(300*sizeof(float)) // arba taip
……
free(a); farfree(b); }
3.20. Dinaminiai masyvai
• Aprašant masyvus kaip rodiklius galima pakeisti masyvui išskirtos atminties kiekį programos vykdymo metu
• Tam daţniausiai naudojamos funkcijos realloc() arba farrealloc();
• Dydis ir vėl nurodomas baitais
• Pvz: short *a;
a=malloc(10);
a=realloc(a,20);
• Pastaba: nepriklausomai nuo masyvo aprašymo būdo, masyvo elementus galima adresuoti abiem anksčiau nurodytais
būdais
4. IV-OJI PASKAITA: Funkcijos.
4.1. Bendros sąvokos apie funkcijas
• Kas yra funkcija ?
• Vieną funkcijos sąvoką ţinote iš matematikos. Ką reiškia funkcija matematikoje ?
• Programavime funkcija turi panašumų, bet turi ir daug esminių skirtumų
• Sąvoka paprogramė (subroutine) naudota daugelyje ankstyvųjų programavimo kalbų (pvz. Fortran, PL/1, Cobol ir
t.t.)
• Paprogramė – tai programos dalis , gebanti atlikti analogiškus veiksmus su skirtingais kintamaisiais
• Funkcija kiek platesnė sąvoka nei paprogramė
4.2. Funkcijos apibrėžimas
• Egzistuoja keletas funkcijos apibrėţimų
• Funkcija – tai operatorių grupė, atliekančių išbaigtą veiksmų seką. Į funkciją galimą kreiptis pagal pavadinimą,
perduoti jai argumentus ir gauti iš jos reikšmę
• Funkcija – tai logikai savarankiška programos dalis, turinti savo pavadinimą, kuriai gali būti perduodami parametrai
ir kuri gali graţinti reikšmę
• Pirmas apbrėţimas techniškesnis, antras – labiau atspindintis reikalo turinį
4.3. Funkcijos
• C kalba parašyta programa – tai iš esmės funkcijų rinkinys. Funkcijos išdėstytos bet kuria tvarka. Faktiškai funkcijos
gali būti išdėstytos keliuose failuose.
• C kalbos koncepcijoje numatyti visi paprogramių tipai: funkcijos ir procedūros
- 18 -
• Funkcijos leidţia rašyti programą moduliniu būdu: kiekviena funkcija atlieką tam tikrą uţdavinį. Tokias programas
ţymiai lengviau skaityti
• Funkcijos leidţia išvengti analogiškų veiksmų dubliavimo
• Daugelis programų gali naudoti tas pačias funkcijas, todėl nereikia perrašinėti kiekvieną kartą.
4.4. Funkcijos aprašymas
Funkcijos aprašymo standartas atrodo taip:
• [duomenu_tipas] funkcijos_vardas(argumentu_sarasas) {
kintamuju aprasymai;
……. // funkcijos_kunas
operatoriai;
[return (israiska)];
}
[] – skliausteliuose pateikti neprivalomi funkcijos elementai
4.5. Funkcijos aprašymo paaiškinimas
• Laukas ―duomenų tipas‖ nurodo funkcijos graţinamos reikšmės tipą jeigu jis nėra nurodytas, pagal nutylėjimą
laikoma, kad funkcija graţina int tipo reikšmę. Jeigu lauke ―duomenų tipas‖ nurodytas raktinis ţodis void, tuomet
funkcija negraţina jokios reikšmės.
• Laukas ―funkcijos pavadinimas‖ – tai ypatingas rodiklio tipas, vadinamas rodikliu į funkciją. Jo reikšmė yra lygi
įėjimo į funkciją pradţios adresui. Funkcijos pavadinimas be to yra identifikatorius ir sudaromas pagal
identifikatoriams C kalboje keliamus reikalavimus.
• Laukas ―argumentų sąrašas‖ apibrėţia argumentus (kartais vadinami parametrais), perduodamus į funkciją.
Argumentų sąrašas gali būti sudarytas iš bet kokios duomenų tipų jų vardų kombinacijos. Argumentų skaičius
neribojamas. Argumentai tarpusavyje skiriami kableliais. Jeigu argumentai nenaudojami, sąraše rašomas ţodis void
4.6. Funkcijos prototipas
• C kalbos standartas reikalauja, kad dar iki pirmo funkcijos iškvietimo programoje būtų pateiktas funkcijos
apibrėţimas
• Toks pirminis funkcijos apibrėţimas vadinamas funkcijos prototipu.
• Prototipas praneša kompiliatoriui apie graţinamos reikšmės tipą, argumentų kiekį ir tipus.
• Po prototipo dedamas kabliataškis;
• Daţnai prototipai talpinami į atskirus failus, turinčius plėtinį .h ir vadinamus header-failais
• Standartinių funkcijų prototipai pateikti header failuose, kurie įtraukiami su direktyva include
4.7. Pirmas funkcijos pavyzdys
• Uţduotis: apskaičiuoti skritulio plotą, kai spindulys r=5,10,27
• Galima taip:
void main() {
int r1=5,r2=10,r3=27;
float s1,s2,s3;
s1=3.14159*r1*r1;
s2=3.14159*r2*r2;
s3=3.14159*r3*r3;
}
Funkcijos nenaudojamos
O galimą ta patį uţrašyti ir taip
float area(int r);
void main() {
int r1=5,r2=10,r3=27;
float s1,s2,s3;
s1=area(r1);
s2=area(r2);
s3=area(r3);
}
float area(int r) {
return(3.14159*r*r); }
Šiuo atveju laimėjimas atrodo nedidelis,
bet jei reikės atlikti tarkim 20 veiksmų
funkcijoje ?
- 19 -
4.8. Formalūs ir aktualūs argumentai
• Kuomet aprašome funkcijos kūną, nurodome argumentų, kurie turėtų būti perduoti į funkciją, sąrašą.
• Šie argumentai vadinami formaliais, su jais veiksmai programos vykdymo metu nebus atliekami
• Kuomet iškviečiame funkciją, nurodome į funkciją perduodamų argumentų sąrašą.
• Šie argumentai (tiksliau – jų kopijos) programos vykdymo metu bus perduoti į funkciją ir su jais atliekami veiksmai.
Jie vadinami aktualiais argumentais.
• Privalo sutapti formalių ir aktualių argumentų tipai.
• Kompiliatorius pasirūpins, kad atitinkamas aktualus argumentas atsidurtų atitinkamo formalaus argumento vietoje.
4.9. Funkcijos iškvietimas
• funkcijos_vardas(argumentų sąrašas)
• Vykdymas perduodamas į funkciją
• Atliekami eilės tvarka funkcijos kūne aprašyti veiksmai
• Įvykdţius sugrįţtama į sekančią po funkcijos iškvietimo einančią operaciją
void intro(void);
int main() {
……
intro();
…..
}
void intro(void) {
printf(“pradedamas programos vykdymas\n”);
printf(“programos autorius jonukas\n”” }
4.10. Funkcijos argumentai
• Naudojami informacijos perdavimui į funkciją
• Į funkciją perduodamos reikšmės, o ne patys kintamieji
• Yra lokaliniai funkcijos kintamieji
• Iškviečiant funkciją reikia nurodyti tikslų jų skaičių, tipus, ir eilės tvarką
int main() {
float amount=250, interest=0.075;
print_growth(amount,interest);
printf(main: amount= %.2f\n‖,amount);
}
void print_growth(float val,float rate) {
val=1+rate)*val;
}
4.11. Tipų kastingavimas funkcijose
• Tipų kastingavimas naudojamas laikinam duomenų tipo pakeitimui
• Naudojamas aiškiam teisingo funkcijos tipo nurodymui
float func(float x);
void func1(int x);
int man() {
int x;
func((float)x);
funcą(x);
}
4.12. Duomenų perdavimas į funkciją
• Parametrų perdavimui į funkciją egzistuoja du stiliai:
1) funkcijų iškvietimas su reikšmių perdavimu (call-by-value);
2) funkcijų iškvietimas su kintamųjų adresų perdavimu (call-by-reference);
- 20 -
• Iškvietimas su reikšmių perdavimu – tai kintamųjų reikšmių kopijų perdavimas į funkcijos kūną. Tai niekaip
neleidţia pakeisti kintamųjų reikšmes iškvietimo taške
• Iškvietimas su adresų perdavimu – tai kintamųjų adresų kopijų perdavimas į funkcijos kūną. Tokiu būdu mes galime
keisti reikšmes saugomas nurodytais adresais
4.13. Kintamųjų reikšmių perdavimo pavyzdys
void suma(int a,int b,int suma);
void main() {
int a=5,b=7,sum=5;
printf(―sum=%d\n‖,sum);
suma(a,b,sum);
printf(―sum=%d\n‖,sum);
}
void suma(int a,int b,int suma) {
suma=a+b;
}
void suma(int a,int b,int suma);
void main() {
int a=5,b=7,sum=5;
printf(―sum=%d\n‖,sum);
suma(&a,&b,&sum);
printf(―sum=%d\n‖,sum);
}
void suma(int *a,int *b,int *suma) {
*suma=*a+*b;
}
4.14. Keli pavyzdžiai
• Kas neteisinga šiame funkcijos aprašyme ?
int compute(int , int ){
int x;
double y;
..... }
Kas bus išspausdinta įvykdţius sekančią programą ?
void square(int val);
int main() {
int x=5;
square(x);
printf(―x lygu %d\n‖,x);
}
void square(int val) {
val=val*val:
printf(―%d\n‖,val); }
4.15. Steko reikšmė
• Stekas – ta atminties sritis, kurią vykdanti programa naudojama laikinam saugojimui;
• Kuomet iškviečiama funkcija, visi šios funkcijos argumentai patalpinami į steką, po to patalpinamos esamų registrų
reikšmės ir kiti parametrai
• Toks argumentų, registrų ir automatinių kintamųjų rinkinys, skirtas vienai funkcijai, vadinamas freimu.
• Jei iškviesta funkcija viduje iškviečią dar vieną funkciją, tai jai sukuriamas dar vienas freimas ir anksčiau steke buvę
parametrai perstumiami tolyn
• C kalba parašytoms programoms reikia daug steko, neretai stekas persipildo ir dėl to gali ―nulūţti‖ programa
4.16. Operatorius return
• Graţina reikšmę į iškviečiančią funkciją return išraiška;
• Nėra privalomas operatorius
- 21 -
• Negraţina jokios reikšmės jeigu naudojama išraiška
return;
int main(){
int len=50, width=4, area;
area=area_rect(len,width);
printf(“plotas lygus %d\n”,area);
return 0; }
int area_rect(int l, in w) {
return l*w; }
4.17. Masyvai kaip funkcijos argumentai
• Į funkcija neperduodama viso masyvo kopija
• Perduodamas tik pirmo elemento adresas
• Per šį adresą funkcija gali prieiti prie masyvo elementų, juos modifikuoti.
void main() {
char current[30], target[0];
...
stringcopy(target,current);
... }
void stringcopy(char str1[], char str2[]) {
int i;
for(i=0; str2[i]!=‗\0‘;i++) str1[i]=str2[i];
str1[i]=‗\0‘;
}
4.18. Vienmačių masyvų perdavimas į funkciją
• Duoti du sveikų skaičių masyvai. Rasti kuriame iš jų yra daugiau teigiamų skaičių
• int n_posit(const int *a, const int n) {
int main() {
int i,n;
int a[50], b[50];
printf(“iveskite elementu skaiciu:”);
scanf(“%d”,&n);
printf(“iveskite elementus:”);
for(i=0;i<n;i++) scanf(“%d %d”,&a[i],&b[i]);
if(n_posit(a,n)>n_posit(b,n)
printf(“pirame daugiau teigiamu elementu);
else if ..
}
int n_posit(const int *a, const int n) {
int i, count=0;
for(i=0;i<n;i++) if(a[i]>0) count++;
return count; }
4.19. Stringų perdavimas į funkcijas
• Parašyti programą, kuri nustatytų kiek simbolių yra eilutėje
int string_elem(char *str);
void main() { char strp[256];
int elem;
printf(“iveskite simboliu eilute”);
gets(strp);
elem=string(elem);
printf(“stringe yra %d eleementu\n”,elem);
}
int string_elem(char *str) {
int elem;
for(i=0;str[i]!=„\0‟;i++)
elem++;
return(elem);
}
- 22 -
4.20. Parametrų perdavimas į funkciją main
• Kai kada reikia perduoti parametrus į pagrindinę funkciją main
• tai naudoja pvz. archyvavimo programos, dirbančios iš komandinės eilutės
• [tipas] main(int argc, char **argv) {
funkcijos kunas
}
arba
• [tipas] main(int argc, char **argv, char **env) {
funkcijos kunas
}
4.21. Standartinės funkcijos
• Kiekvienoje C programų kūrimo sistemoje yra daug standartinių funkcijų
• Su visa eile standartinių funkcijų jau teko susitikti: printf, scanf
• Šios funkcijos yra sudėtos į bibliotekas, jų prototipai – į header-failus, kuriuos reikia įtraukti naudojant direktyvą
include
• Standartinės funkcijos sugrupuotos pagal tematiką į grupes, programuotojui reikėtų pasirūpinti reikalingų funkcijų
paieška
• Pats programuotojas taip pat gali kurti nuosavas funkcijų bibliotekas
4.22. Parametrų perdavimas į funkciją main
• Komandinės eilutės ţodţiai tai transformuotos į ASCII formatą simbolių sekos, atskirtos tarpais, uţduodamos
programos paleidimo metu
• Programos aplinka – tai seka ASCII simboliais išreikštų sekų, kuri tam prieinama programai, jos paleidimo metu
• Į funkciją perduodamų parametrų prasmė:
int argc – ţodţių skaičius komandinėje eilutėje
char **argv – rodiklis į rodiklių masyvą iš argc elementų. nulinis elementas – visada programos pavadinimas
char **env – rodiklis į kintamo dydţio masyvų rodiklį
Pavyzdys programos su argumentų perdavimu į funkciją main:
#include <stdio.h>
void main(int argc, char **argv) {
printf(“i programa perduota %d argumentu\n”,argc);
printf(“ perduoti duomenys irasyti faile %s\n”, argv[1]);
printf(“paleista programa vadinasi %s\n”, argv[0]);
}
4.23. Rekursinės funkcijos
• C kalba leidţia funkcijai iškviesti pačiai save.
• Funkcija, besikreipianti pati į save vadinama rekursija
• Kiekvienu atveju steke išskiriama atminties sritis saugoti kintmųjų kopijas ir kitus parametrus
• Klasika tapusi faktorialo skaičiavimo funkcija:
f(n) {
if(n) return n*f(n-1);
else return 1;
}
- 23 -
5. V-OJI PASKAITA. Priešprocesorius. Grafika.
5.1. Kompiliacijos etapai
• Priešprocesorius (include failai)
• Išplėstinis Pradinis programos kodas
• programos kodas C kalba
• C kompiliatorius
• Kodas asemblerio kalba
• Asemblerio kompiliatorius
• Objektinis kodas
• Linkeris, objektų biblioteka
• Vykdomas kodas
5.2. Priešprocesorius
Praktiškai kartu su kiekvienu C kalbos kompiliatoriumi pateikiamas taip vadinama priešprocesorius (angl. preprocessor)
Priešprocesorius ieško jam skirtų direktyvų dar iki programos kompiliavimo pradţios ir atlieka veiksmus, kurių reikalauja
panaudotos direktyvos.
Priešprocesoriaus direktyvų vykdymas – tai atitinkamas programos išeities kodo sutvarkymas bet jokiu būdu ne
kompiliavimas, t.y. Programos kodo vertimas į mašinines komandas
Preišprocesorius – tai galinga priemonė, leidţianti pagerinti programos skaitymą
Visos priešprocesoriaus direktyvos pradedamos komanda #
5.3. Priešprocesoriaus apibendrinimas
• Tai pirmas kompiliavimo etapas
• Pranašumai:
Skaitymo patogumas
Programos palaikymo palengvinimas
Lankstumas
Mobilumas
• Direktyvos:
Pradedamos simboliu #
Gali būti bet kurioje programos vietoje, nors daţniausiai naudojamos pradţioje
5.4. Prešprocesoriaus priemonės
Priemonės:
• Komentarų pašalinimas
• Konstantų ir makrosų (arba makrokomandų) apibrėţimas
• Failų įtraukimas (file inclusion)
• Sąlyginė kompiliacija (conditional compilation)
5.5. Simbolinės konstantos
• Apibrėţus simbolinę konstantą, priešprocesorius pakeičia visas faile aptiktas nuorodas į šią konstantą jos faktine
reikšme
• Negalima pakeisti komentarų arba dvigubų kabučių
• Pvz. , jeigu programoje buvo apibrėţta simbolinė konstanta vardu MAXITEMS , visur, kur programoje
priešprocesorius aptiks tokiu vardu esančią konstantą, jis pakeis ją priskirta reikšme
• Reikšmė turi būti nurodyta aprašant simbolinę konstantą
• Simbolinės konstantos aprašymas:
#define identifikatorius eilutė-leksema
• Simbolinių konstančių pavadinimams
priimta naudoti tiktai didţiąsiąs
raides
#define TRUE 1
#define FALSE 0
#define MAXITEMS 500
int main() {
int i, found, val[MAXITEMS];
found=FALSE;
while(found==FALSE) {
….
if(i<MAXITEMS) { …
found=TRUE; } }
for (i=0;i<MAXITEMS;i++) … ;}
}
- 24 -
5.6. Makrosai
• Makrosai (taip pat daţnai vadinami makrokomandomis) – tai trumpa programa, kuriai gali būti perduoti argumentai
• Sintaksė panaši kaip ir simbolinių konstantų, tačiau makrosuose egzistuoja eilė parametrų, einančių iš karto po
identifikatoriaus
• Tie parametrai gali būti argumentų pavadinimai
• Tarp identifikatoriaus ir atidarančio skliaustelio ( jokiu būdu negali būti tarpo
• Kuomet priešprocesorius išskleidţia makrosą, jis atitinkamoje vietoje įstato kodą su atitinkamais parametrais
5.7. Makrosų tęsinys
• Jeigu makrokomandos uţima daugiau negu vieną eilutę, reikia naudoti simbolį \ jų apjungimui
• Keliems sakiniams naudokite bloką
#define SWAP(A,B) { int temp; \
temp=A; \
A=B; \
B=temp; }
int main() {
int num1=30, num2=50;
if(num2>num1) SWAP(num1,num2);
}
• Leksemos makrokomandose gali būti apjungtos naudojant operatorių ##
• Makrososuose naudojami trigrafai – trijų simbolių sekos, kurios reiškia vieną simbolį
• Trigrafai naudojami norint surinkti simboliu, kurių nėra kai kuriose klaviatūrose:
• ??= # ??> } ??- ~
?? ( [ ??/ \
?? ) ] ??‘ ^
?? < { ??! |
#define WheatBread 0
#define RyeBread 1
#define WhiteBread 2
#define PumopernickeBread 3
#define Bread(x) x## Bread
int main() {
printf(―Value of WheatBread is %d\n‖, Bread(Wheat));
printf(―Value of RyeBread is %d\n‖, Bread(Rye));
printf(―Value of WhiteBread is %d\n‖, Bread(White));
printf(―%c\n‖, ‗??-‘);
}
5.8. Funkcijų ir makrokomandų palyginimas
• Greitaeigiškumas:
1) Makrosai greitesni, įterpiami atitinkamose vietose
2) Funkcijos lėtesnės, naudoja steką
• Vykdomos programos dydis:
1) Maţesnis, jei naudojamos funkcijos, kodas pasikartoja tik vieną kartą
• #define identifikatorius(arg[,arg],..)
eilutė-leksema
• Trumpa paprogramė, priimanti
argumentus
• Priimta naudoti didţiąsias raides
• Įtraukiami į kabutes, jei naudojami
kartu su operatoriais
#define SQUARE(X) ((X)*(X))
#define MAX(A,B) ((A)>(B) ? (A):B))
int main() {
int int1, int2;
int1=SQUARE(3);
int2=SQUARE(int1+1);
printf(―max=%d\n‖, MAX(int1,int2));
}
Kas isvesta bus ?
- 25 -
• Kiti faktoriai:
1) Funkcijos gali sugraţinti reikšmę operatoriuje return, makrosai negali
2) Makrosai neleidţia rekursijos
3) Makrosai daţnai sudėtingesni programos derinimo metu
5.9. Failų įtraukimas
#include <filename.xxx>
#include ―filename.xxx‖
Į programą įtraukiama nurodyto failo kopija: aptikęs šią direktyvą priešprocesorius toje vietoje įdės nurodyto C kalba
parašyto failo kopiją
Daţnai naudojama header failų įtraukimui.
Header failai turi funkcijų prototipus
Gali būti naudojama ir bet kokių kitų programų failų įtraukimui į kompiliuojamą išeities programą
5.10. Naujo duomenų tipo pavadinimo apibrėžimas
• Komanda typedef – tai naujo, sinoniminio, pavadinimo suteikimas egzistuojančiam duomenų tipui
• typedef egzistuojantis_tipas naujas_tipas
• Naudojama skaitomumui ir mobilumui pagerinti
• Sintaksė analogiška kintamųjų aprašymui
• Rekomenduojama naudoti didţiąsias raides
typedef char BYTE;
typedef unsigned short USHORT;
typedef int WORD;
int main() {
BYTE input;
WORD buf[512];
USHORT a;
}
5.11. Sąlyginė kompiliacija
• #if, #else, #elseif, #endif, #ifdef, #ifndef -tai sąlyginės kompiliacijos direktyvos
• Leidţia sukurti keletą tos pačios programos versijų
• Kompiliuojama ta versija, kuriai patenkinamos uţduotos sąlygos
• Sąlygos paprastai uţduodamos naudojant direktyvą define
• Kai kuriais atvejais gali būti naudojamos kitos direktyvos
#define WINDOWS32
int main() {
#ifdef WINDOWS32
printf(― Programa skirta Win32 operacinei aplinkai\n‖);
#endif
#ifdef UNIX
printf(―programa skirta Unix operacinei aplinkai\n‖);
#endif
….. }
5.12. Kontroliniai pratimai
• Raskite klaidą šiame fragmente:
#define LINELEN 80;
int main() {
char line[LINELEN];
int x;
….
Kas padaryta neteisingai apibrėžiant makrosą
ISDIGIT:
#include <stdio.h>
#define ISDIGIT(C) return((C)>=„0‟ &&
(C)<=„9‟)
int main() {
int input, digits=0;
input=getchar();
if(ISDIGIT(input)) digits++;
- 26 -
5.13. Darbas su grafika
• Informacija į standartinį išvedimo įrenginį (daţniausiai monitoriaus ekraną) gali būti išvedama dviem skirtingais
reţimais: tekstiniu ir grafiniu.
• Grafiniame reţime informacija išvedama taškais, tekstiniame – simboliais
• Bet kuriuo atveju išvedimas, ypač grafiniame reţime, labai priklauso nuo techninės įrangos
• Negalima padaryti universalių, nepriklausančių nuo aparatinės įrangos, išvedimo modulių.
• Todėl C kalboje nėra specialių grafikos apdorojimo metodų, o grafiniam išvedimui naudojamos standartinių
bibliotekų funkcijos
• Esminis elementas grafikos apdorojime – draiverio sąvoka
• Draiveris – tai speciali programa, atliekanti tarpininko vaidmenį, t.y. Perimanti programos pateikiamus duomenis,
atitinkamu būdu transformuojanti juos, ir perduodanti techniniams įrenginiams, kurie išveda atitinkantį grafinį vaizdą
į ekraną.
• Kaip taisyklė, savo programoje autorius turi nurodyti kokį grafinį draiverį reikia naudoti !!!
• Programiniu poţiūriu, bazinis grafikos elementas vadinamas pikseliu (sutrumpinimas nuo picture element).
• Pikselių kiekis uţduoda naudojamą grafinę rezoliuciją.
• Kokius grafinius reţimus (rezoliucijas) ţinote ?
5.14. Darbas su grafinėmis funkcijomis
• Labai priklauso nuo konkrečios programavimo aplinkos
• Mūsų funkcijos aprašytos faile graphics.h , todėl reikia naudoti direktyvą:
#include <graphics.h>
• Programiniu poţiūriu grafiko programavimas: nurodyti ką su kiekvienu ekrano pikseliu reikia daryti
• Programavimą visada sudaro trys pagrindiniai etapai:
1. Grafinio įrenginio atidarymas
2. Grafikos apdorojimo veiksmų programavimas
3. Grafinio įrenginio uţdarymas
5.15. Grafinio įrenginio atidarymas
• Grafinio įrenginio inicializavimo funkcija:
• void initgraph(int *graphdriver, int *graphmode chat *pathtodriver);
graphdriver – skaičius, ţymintis grafinio draiverio pavadinimą. Galima naudoti simbolinius pavadinimus DETECT, CGA,
VGA, MCGA ir t.t.
gmode – kintamasis, reiškiantis pradinį grafinį reţimą
pathtodriver – kelias iki direktorijos, kur įrašyti grafiniai draiveriai. Pas mus c:\bc\bgi
Pvz: int gdriver=DETECT, gmode;
initgraph(&gdriver, &gmode, ‖c:\\bc\\bgi‖);
5.16. Grafinio įrenginio uždarymas
• Pabaigus darbą su grafiniu reţimu, reikia uţdaryti grafinį įrenginį
• Uţdarymui naudojama funkcija closegraph:
• void closegraph(void);
• Pvz:
#include <graphics.h>
void main() {
int gdriver=detect, gmode;
initgraph(&gdriver, &gmode, ”c:\\bc\\bgi”);
…. // cia rasomi garfikos apdorojimo veiksmai
closegraph();
}
5.17. Kai kurios grafinės funkcijos
Ekrano išvalymo funkcija:
• void cleardevice(void);
Kursoriaus pozicijos valdymo funkcijos:
• void moveto(int x, int y);
• void moverel(int dx, int dy);
Tiesių braižymo funkcijos:
• void line(int x1, int y1, int x2, int y2);
- 27 -
• void linerel(int dx, int dy);
• void linerel(int x, int y);
• Vieno pikselio išvedimo funkcija:
• void putpixel(int x, int y, int color);
Grafinių figūrų paišymo funkcijos:
• void rectangle(int left, int top, int right, int bottom);
• void circle(int x, int y, int radius);
• void arc(int x, int y, int stangle, int endangle, int radius);
• void drawpoly(int numpoints, int *polypoints);
• void bar(int left, int top, int right, int bottom);
• void fillpoly(int numpoints, int *polypoints);
5.18. Spalvų valdymo funkcijos
• Fono spalvos nustatymas: void setbkcolor(int color);
• Objekto spalvos nustatymas: void setcolor(int color);
• Spalva – tai sveikas skaičius iš intervalo 0 – 15 (kur 0 – juoda spalva, 15 – balta spalva).
• Be to galima naudoti šiuos simbolinius spalvų pavadinimus:
• BLACK , BLUE, GREEN, CYAN, RED, MAGENTA, BROWN, LIGHTGRAY, DARKGRAY, LIGHTBLUE,
LIGHTGREEN, LIGHTCYAN, LIGHTRED, LIGHTMAGENTA, YELLOW, WHITE.
• Atskiras spalvos modifikatorius: BLINK
5.19. Teksto valdymas grafiniame režime
Teksto išvedimo funkcijos:
• outtext(char *textstring);
• outtextxy(int x, int y, char *textstring);
Teksto dydžio, stiliaus valdymo funkcijos:
• void setextstyle(int font, int direction, int charsize);
• Font: TRIPLEX_FONT, SMALL_FONT, SANS_SERIF_FONT, GOTHIC_FONT
• Direction: HORIZ_DIR, VERT_DIR
• Charsize: 0,1 (8x8), 2 (16x16), 3 (32x32) ir t.t.
5.20. Paprastos programos su grafika pavyzdys
#include <graphics.h>
#include <conio.h>
void main() {
int gdriver=detect, gmode;
initgraph(&gdriver, &gmode, ”c:\\bc\\bgi”);
moveto(150,200);
lineto(200,250);
line(100,200,150,350);
setcolor(red);
rectangle(100,100,300,400);
outtextxy(150,275,”as parasiau”);
getch();
closegraph();
}
- 28 -
6. VI-OJI PASKAITA. Matomumo sritis. Kintamųjų klasės.
6.1. Matomumo sritis
• Aprašėme programoje kintamąjį, tegul int a. Kurioje programos dalyje šis kintamasis yra matomas, tai yra juo galima
naudotis (priskirti reikšmes, naudoti operanduose ir pan.) ?
• C kalboje egzistuoja pagrindinė taisyklė: visi kintamieji yra matomi blokuose.
• Blokas – tai programos dalis, apimta riestiniais skliausteliais { }
• Kiek blokų gali būti vienoje programoje ?
• Kokio tipo blokai daţniausiai sutinkami programoje ?
• Daţniausiai pasitaikantis blokas programose yra funkcija, tokiu atveju ir kintamasis yra matomas funkcijos ribose
• Tačiau tai nėra privaloma
• Funkcijos viduje galima padaryti atskirus blokus: t.y. kintamasis bus matomas tik dalyje funkcijos
• Tačiau toks skaidymas naudojamas retai.
• Ţymiai daţniau naudojami globaliniai kintamieji – kintamieji , kurie yra matomi ne tik vieno bloko viduje, bet
platesnėje programos dalyje
6.2. Lokaliniai ir globaliniai kintamieji
• Pasikartosime: kintamieji apibrėţti bloke yra matomi tik to bloko ribose
• Tokie kintamieji vadinami lokaliniais
• Jų priešingybė – globaliniai kintamieji
• Globaliniai kintamieji yra apibrėţiami uţ bloko ribų (todėl nepriklauso jokiam blokui) ir yra matomi ...
• Kur yra matomi globaliniai kintamieji
• Priklauso nuo apibrėţimo: paprastai uţ bloko ribų aprašyti kintamieji yra matomi duoto failo ribose.
• Matomi visuose blokuose, t.y. visose tame faile aprašytose funkcijose
6.3. Globaliniai kintamieji
• Globaliniai kintamieji – vienas iš būdų perduoti į funkciją kintamuosius ir gauti iš funkcijos reikšmes.
int a;
void main() {
int c,b; a=7; b=5;
c=a+b;
func(c); printf(―%d\n‖,a); }
void func(int c) {
int b;
b=a+c; a=b; }
void main() {
int a, b, c;
{
int d;
a=7; b=2*a;
c=a|b;
d=c*b; // ar gerai ?
}
a=d/3+b; // ar gerai ?
}
void main() {
int a, b, c;
{
int d;
a=7; b=2*a;
c=a|b;
d=c*b; // ar gerai ?
}
d=d/3+b; // ar gerai ?
}
int a;
void main() {
int c,b;
a=7; b=5;
c=a+b;
func(); }
void func() {
int c,b;
c=a; b=c; }
void main() {
int a, c,b;
a=7; b=5;
c=a+b;
func(); }
void func() {
int c,b;
c=a; b=c; }
- 29 -
6.4. Lokalinių ir globalinių kintamųjų vardų sutapimas
• Neretai pasitaiko situacijo, kuomet programoje yra lokalinių ir globalinių pavadinimų tais pačiais vardais:
int a,b;
void main() {
int c; a=7; b=5;
c=a+b;
func(c); printf(―%d\n‖,a); }
void func(int c) {
int b;
b=a+c; a=b; }
Jeigu yra lokalinis ir globalinis kintamasis tuo pačiu vardu, pagal nutylėjimą naudojamas lokalinis kintamasis.
• Jeigu norime panaudoti globalinį kintamąjį, prieš jo pavadinimą reikia parašyti modifikatorių :: (du dvitaškiai):
int a,b;
void main() {
int c; a=7; b=5;
c=a+b;
func(c); printf(―%d\n‖,a); }
void func(int c) {
int b;
b=a+c; ::b=b; }
6.5. Atminties klasių apžvalga
• Atminties klasės – tai alternatyvūs duomenų saugojimo variantai
• Atminties klasės:
o automatiniai
o registriniai
o išoriniai
o statiniai
• Veikimo sritis: sakiniai, kuriuose galima naudoti nurodytą kintamąjį
• Gyvavimo laikas: laiko tarpas, kurio metu išsaugoma kintamojo reikšmė
6.6. Automatiniai kintamieji
• Veikimo sritis: lokaliniai, blokas, aprašomi naudojant raktinį ţodį auto
• Gyvavimo laikas: sukuriami įeinant į bloką. Nustoja gyvuot (sunaikinami) išeinant iš bloko
• Vidiniai aprašymai išstumia išorinius
void print_cube(int);
int main(){
int x;
x=getchar():
if(x==„c‟) { ….
}
print_cube(100); }
void print_cube(int x) { // x zinoma tiktai funkcijoje print_cube
printf(“%d\n”,x*x*x); }
6.7. Registriniai kintamieji
• Registriniai kintamieji naudojami siekiant pasiekti aukštesnį programos veikimo našumą
• Aprašomi naudojant raktinį ţodį register
• Kompiliatoriui generuojama uţklausa, kad šis kintamasis būtų saugomas ne steke, o registre (kitaip sakant
procesoriuje)
• Ta automatinių kintamųjų poaibis, todėl gali būti aprašyti tik lokaliniai kintamieji
• Registrų nedaug, galima priskirti tik tuos kintamuosius, kurie tikrai naudojami programoje labai daţnai
• Patartina tokia galimybe naudotis tik labai patyrusiems programuotojams, nes naujokams tai kaip taisyklė neduos
jokios naudos.
• Šiuolaikiniai kompiliatoriai labai efektyviai optimizuoja registrinių kintamųjų priskyrimo procesą
• Greitis ir efektyvumas
• Naudojami procesoriaus registrai vietoj steko kintamųjų saugojimui
• Naudotini funkcijų parametrams ir automatiniams kintamiesiems
- 30 -
• Matomumo sritis ir gyvavimo laikas: kaip automatiniams kintamiesiems
• Leidţiamų registrų skaičius priklauso nuo procesoriaus tipo
f(register int count, register int num) {
register int i;
for(i=0;i<count;i++) num--; }
6.8. Išorinė atminties klasė
• Išoriniai kintamieji aprašomi ne funkcijos viduje. Jie prieinami visoms funkcijoms, kurios randasi faile ţemiau
kintamojo aprašymo
• Ypatybė: išoriniai kintamieji pagal nutylėjimą inicializuojami 0, masyvai – nuliniais baitais.
• Pranašumas – išoriniai kintamieji uţtikrina skirtingoms funkcijoms bendrą naudojimą duomenimis. Tai greitesnis
būdas, nei naudoti argumentų sąrašo perdavimą į funkciją.
• Išorinių kintamųjų pavadinimams naudojami tos pačios taisyklės kaip ir paprastų kintamųjų pavadinimų sudarymui
• Programos skaitymas ir palaikymas daţnai sudėtingesnis negu naudojant vidinius kintamuosius
6.9. Išoriniai kintamieji
6.10. Keletas programos failų
• Daţnai didelių programų tekstas (išeities kodas) rašomas ne viename, o keliuose failuose
• Kartais failų skaičius gali būti ir labai didelis
• Kelių failų naudojimas palengvina redagavimo, kompiliavimo ir derinimo procesą
• Išoriniams kintamiesiems yra skirtumas tarp apibrėţimo ir aprašymo
• Apibrėţimas yra vienas, o aprašymas nurodo atminties sritį.
• Norint naudotis kituose failuose aprašytais globaliniais kintamaisiais naudojamas raktinis ţodis extern
6.11. Programa keliuose failuose
6.12. Statinė atminties sritis
• Statiniai kintamieji daug kuo panašūs į išorinius: jie inicializuojami 0 reikšmėmis
• Išorinis statinis kintamasis aprašomas ne bloko ribose, tačiau yra matomas tik to failo ribose. Joks kitas failas jokiu
būdu negali prieiti prie šio kintamojo
• Globaliniai kintamieji
• Aprašomi ne funkcijų viduje
ir įgyja nulines reikšmes
aprašymo metu
• Matymo sritis: nuo aprašymo
vietos iki failo pabaigos
• Gyvavimo laikas: programos
gyvavimo laikas
• Pranašumas: bendras
duomenų naudojimas
• Trūkumas: galimi konfliktai,
―privatumo‖ praradimas
int status;
char sysname[20];
void fillbuf(void), emptybuf(void);
int main(){
fillbuf();
…
empytybuf();
}
char buf[1024];
void fillbuf(void) {
… }
void emptybuf(void) {
…. }
one.c
int status;
char sysname[20];
void fillbuf(void);
void emptybuf(void);
main() {
fillbuf();
if(expression) empybuf(); }
two.c
extern int status;
extern char sysname[];
char buf[1024];
void fillbuf(void) {
}
void emptybuf(void) {
...
fillbuf();
}
- 31 -
• Vidinis statinis kintamasis aprašomas bloko viduje ir tik jame matomas. Tačiau egzistuoja visą programos gyvavimo
laiką. Tai yra sugrįţus atgal į bloką (daţniausiai funkciją) jis išsaugo savo reikšmę.
• Vidiniai statiniai kintamieji: pirmą kartą nulis, kitus kartus – prieš tai buvusi reikšmė
6.13. Statiniai kintamieji
• Matymo sritis:
Išoriniai kintamieji:
aprašymas iki failo pabaigos
neprieinama kitiems failams
Vidinis kintamasis:
funkcija, blokas
• Pagal nutylėjimą : inicializuojama reikšmė nulis
• Pranašumai: pastovumas ir ―privatumas‖
6.14. Masyvų inicializacija
• Automatiniai masyvai: pagal nutylėjimą pradinės masyvų reikšmės yra neapibrėţtos
• Išoriniai ir statiniai masyvai: pagal nutylėjimą reikšmės lygios 0 arba NULL
char aplpha[10];
char beta[10]={„a‟,‟b‟,‟c‟,‟d‟};
char gamma[]=“this is gamma”;
int num1[10];
int main(){
static char local[];
int length[10];
6.15. Atminties klasių charakteristikos
Klasė Matymo sritis Gyvavimo
laikas
saugojimas inicializavimas Pradinė
reikšmė
automatinis blokas bloko viduje stekas taip nėra
registrinis blokas bloko viduje registras ne nėra
išorinis failas programa duomenų sritis taip 0
statinis
išorinis
failas programa duomenų sritis taip 0
statinis vidinis blokas programa duomenų sritis taip 0
void main() {
int a=10;
func(a);
….
func(a);
}
void func(int a) {
int i,sum=0;
for(i=0;i<a;i++) sum+=i;
printf(“sum=%d\n”,sum);}
void main() {
int a=10;
func(a);
….
func(a);
}
void func(int a) {
int i; static int sum;
for(i=0;i<a;i++) sum+=i;
printf(“sum=%d\n”,sum);}
- 32 -
6.16. Kai kurie papildomi klausimai
• Greitesni for ciklai:
• Paprastai for( i=0; i<10; i++){ ... }
• Operacijų tvarka 0,1,2,3,4,5,6,7,8,9
• Jeigu nesvarbi operacijų vykdymo tvarka, ciklą galima rašyti taip:
• for( i=10; i--; ) { ... }
• Tvarka 9,8,7,6,5,4,3,2,1,0, ir ciklas bus šiek tiek greitesnis
Yra paprasčiau realizuoti "i--" kaip sąlygą ―nelygu nuliui? Jei taip, atlikti dekremento operaciją ir tęsti".
• Taip pat galima: for(i=10; i; i--){} ir for(i=10; i!=0; i--){}
• Niekada nenaudokite dviejų ciklų, kur pakanka ir vieno ciklo
• Pavyzdţiui turime tokią programos konstrukciją. Viskas teisinga, bet ...
• for(i=0; i<100; i++) { stuff(); }
for(i=0; i<100; i++) { morestuff(); }
Geriau taip:
for(i=0; i<100; i++) { stuff();
morestuff(); }
• Jeigu cikle yra nedaug iteracijų, gal verta ciklą paaukoti visai ir tas pačias operacijas, atliekamas cikle, išvardinti
atskirai
• Pavyzdţiui vietoj: for(i=0; i<3; i++) { something(i); }
• Gal verta panaudoti:
something(0);
something(1);
something(2);
6.17. Kai kurie optimizavimo klausimai
• Jei reikia cikle ką nors rasti,
tuomet įvykdţius sąlygą
patartina nutraukti tolesnį ciklo
vykdymą:
• found = FALSE;
for(i=0;i<10000;i++)
{
if( list[i] == -99 )
{
found = TRUE;
}
}
if( found ) printf("Yes, there is
a -99. Hooray!\n");
• found = FALSE;
for(i=0; i<10000; i++)
{
if( list[i] == -99 )
{
found = TRUE;
break;
}
}
if( found ) printf("Yes, there is a -99.
Hooray!\n");
• Tarkim turime
void func1( int *data ) { int i;
for(i=0;i<10;i++) {
somefunc2(*data, i); } }
Galbūt *data reikšmė nesikeis
kiekvieną kartą , tačiau kompiliatorius
to negali ţinoti ir kiekvieną kartą iš
steko nuskaito *data reikšmę
• Geriau tuos pačius veiksmus
uţrašyti sekančiai:
void func1( int *data ) {
int i; int localdata;
localdata = *data;
for(i=0; i<10;i++) {
somefunc2( localdata,i); }
Tokį uţrašą lengviau optimizuoti
• Kartais gali pasitaikyti tokio tipo
sąlyga:
switch ( queue ) {
case 0 : letter = 'W'; break;
case 1 : letter = 'S'; break;
case 2 : letter = 'U'; break; }
Arba
if ( queue == 0 ) letter = 'W';
else if ( queue == 1 ) letter = 'S';
else letter = 'U';
Bet geriausia būtų parašyti taip:
static char *classes="WSU";
......
letter = classes[queue];
- 33 -
6.18. Keletas optimizavimo patarimų
• In general, savings can be made by trading off memory for speed. If you can cache any often used data rather than
recalculating or reloading it, it will help. Examples of this would be sine/cosine tables, or tables of pseudo-random
numbers (calculate 1000 once at the start, and just reuse them if you don't need truly random numbers).
• Avoid using ++ and -- etc. within loop expressions, eg. while(n--){}, as this can sometimes be harder to optimise.
• Minimize the use of global variables.
• Declare anything within a file (external to functions) as static, unless it is intended to be global.
• Use word-size variables if you can, as the machine can work with these better ( instead of char, short, double,
bitfields etc. ).
• Don't use recursion. Recursion can be very elegant and neat, but creates many more function calls which can become
a large overhead.
• Avoid the sqrt() square root function in loops - calculating square roots is very CPU intensive.
• Single dimension arrays are faster than multi-dimensioned arrays.
• Compilers can often optimise a whole file - avoid splitting off closely related functions into separate files, the
compiler will do better if can see both of them together (it might be able to inline the code, for example).
• Single precision maths may be faster than double precision - there is often a compiler switch for this.
• Floating point multiplication is often faster than division - use val * 0.5 instead of val / 2.0.
• Addition is quicker than multiplication - use val + val + val instead of val * 3
• puts() is quicker than printf(), although less flexible.
• Use #defined macros instead of commonly used tiny functions - sometimes the bulk of CPU usage can be tracked
down to a small external function being called thousands of times in a tight loop. Replacing it with a macro to
perform the same job will remove the overhead of all those function calls, and allow the compiler to be more
aggressive in it's optimization..
• Binary/unformatted file access is faster than formatted access, as the machine does not have to convert between
human-readable ASCII and machine-readable binary. If you don't actually need to read the data in a file yourself,
consider making it a binary file.
• Last but definitely not least - turn compiler optimization on! Seems obvious, but is often forgotten in that last minute
rush to get the product out on time. The compiler will be able to optimize at a much lower level than can be done in
the source code, and perform optimizations specific to the target processor.
- 34 -
7. VII-OJI PASKAITA
Struktūros. Sąjungos. Išvardijimai
7.1. Agregatiniai kintamieji
• Iki šiol naudojome tik paprastus kintamuosius: tik iš vieno tipo sudarytus kintamuosius.
• Paprasti kintamieji ir masyvai
• Tikslas: struktūrų ir apjungimų panaudojimas skirtingų tipų duomenų saugojimu naudojant vieną identifikatoriaus
pavadinimą
• Aprašymas, inicializavimas ir naudojimas:
struktūrų
struktūrų masyvų
rodiklių į struktūras
• Aprašymas, inicializavimas ir naudojimas:
apjungimų
apjungimų masyvų
rodiklių į apjungimus
7.2. Struktūrų apžvalga
• Struktūra gali būti įsivaizduojama kaip įrašas, kurį sudaro laukai arba elementai
• Struktūros naudojamos giminingų kintamųjų grupių aprašymui
• Vartojimas labai priklauso nuo vartotojo: vieni mėgsta, kiti – ne
• Skirtumas nuo masyvų: gali apjungti skirtingo tipo kintamuosius
• Masyvai – tai išvestinis duomenų tipas, struktūros – tai agregatinis duomenų tipas (agregatas)
• Agreguotu vadinamas toks tipas , kurį sudaro keli nepriklausomi duomenų tipai (arba duomenų blokai)
• Kas yra struktūra ?
―Įrašas‖
Rinkinys iš vienos arba kelių kintamųjų, galbūt ir skirtingo tipo, sugrupuotų į vieną bloką ir trinčių vieną vardą
Išvestinis sudėtinis duomenų tipas
• Struktūrų pranašumai:
Naudojamos duomenų grupavimui
Suteikia galimybę apdoroti grupę giminingų kintamųjų kaip vieną visumą
Pavardė id. nr. atlyginimas
7. 3. Struktūros šablonas ir aprašymas
• Skirtingai nei paprasti duomenų tipai, struktūra yra sudėtinis tipas, sukuriamas programuotojo iš egzistuojančių
duomenų tipų, naudojant šabloną.
• Šablonas – iš esmės kintamųjų aprašymų sąrašas
• Šablono matymo sritis analogiška kintamojo matymo sričiai (globalinis arba lokalinis)
• Jeigu aprašan struktūrą naudojamas tegas, tai tokio tipo struktūros gali būti aprašomos likusioje failo dalyje
• Struktūros elementai atmintyje saugomi ta pačia tvarka, kaip ir yra išvardinami aprašant
• Struct [tegas] { elementų sąrašas }
struct emp {
char name[21];
char id[8];
double salary; };
int main() {
struct emp prgmr;
int num;
... }
int f(void) {
struct emp supervisor;
}
- 35 -
7.4. Nuorodos į struktūros elementus
7.5. Struktūrų masyvai
• Struktūrų masyvai panašūs į kitus masyvus
• Vienas skirtumas: struktūrų masyvo elementas yra struktūra
• Staff[0] - ta struktūra
• Staff[0].name - tai struktūrų masyvo elemento laukas
• Staff[0].name[4] – tai penktas lauko name elementas pirmos masyvo struktūros
• Struktūrų masyvai naudojami gana plačiai
7.6. Rodikliai į struktūras
• Rodiklis į struktūrą aprašomas taip pat kaip ir kiti rodikliai: naudojant operatorių *
• struct emp *sp;
• Priėjimui prie struktūros, kuri parašyta kaip rodiklis į struktūrą, laukelių naudojamas specialus operatorius ->
• Šis operatorius nenaudojamas niekam kitam, nes rodikliai į struktūras naudojami labai daţnai
• sp->salary=2750.50;
• Operatorius . (taškas) naudojamas
priėjimui prie struktūros
elementų
#include <stdio.h>
#include <stdlib.h>
struct emp {
char name[21];
char id[8];
double salary; };
int main() {
struct emp prgmr;
char buf[256];
….
gets(prgmr.name);
gets(prgmr.id);
gets(buf);
prgmr.salary=atof(buf);
printf(―vardas: %s\n‖,prgmr.name);
….
prgmr.salary*=1.15;
}
int main() {
struct emp staff[NUM_EMPS];
int i;
double sal_tot=0;
fill_array(staff, NUM_EMPS];
for(i=0;i<NUM_EMPS;i++)
sal_tot+=staff][i].salary;
printf(―Bendras atlyginimas: %.2f‖
sal_tot);
}
#include <stdio.h>
#define NUM_EMPS 100
void fillarray(struct emp *, int );
struct emp {
char name[21];
char id[8];
double salary;
};
#include <stdio.h>
#define NUM_EMPS 100
void fillarray(struct emp *, int );
struct emp {
char name[21];
char id[8];
double salary;
};
int main() {
struct emp staff[NUM_EMPS], *sp;
int i;
double sal_tot=0;
fill_array(staff, NUM_EMPS];
for(sp=staff;
sp!=&staff[NUM_EPS];sp++)
sal_tot+=sp->salary;
printf(―Bendras atlyginimas: %.2f‖
sal_tot);
}
- 36 -
7.7. Alternatyvūs struktūrų aprašymo būdai
• Tego uţdavimas ir kintamųjų aprašymas toje pačioje vietoje
struct emp { char name[12];
char id[8];
double salary;
} prgmr employe[100], *p;
• Aprašymas be tego
struct { char name[12];
char id[8];
double salary;
} prgmr, employe[100], *p;
• Tipo modifikatoriaus panaudojimas
typedef struct { char name[12];
char id[8];
double salary;
} EMPLOYEE;
EMPLOYEE prgmr,employe[100], *p;
7.8. Struktūros ir funkcijos
7.9. Pratimas – rodikliai į struktūras
• Prieš tai pateikta programa būtų ţymiai efektyvesnė, jeigu į funkciją raise() būtų perduotas rodiklis į struktūrą, o ne
pati struktūra Pakeiskite brūkšnelius, kad į funkciją būtų perduodamas rodiklis į struktūrą
#include <stdio.h>
#include ―emp.h‖
void main() {
struct emp progmr;
----- raise(struct emp ---, double);
…..
printf(―senas atlyginimas: %.2f\n‖, prgmr.salary);
prgmr=raise(---- prgmr, 0.12);
printf(―naujas atlyginimas: %.2f\n‖, prgmr.salary);
}
------ raise(struct emp ---person, double increase) {
person---salary*=(1+increase);
return person;
}
• Struktūra gali būti perduodama į
funkciją ir funkcija gali graţinti
struktūrą
#include <stdio.h>
#include ―emp.h‖
void main() {
struct emp progmr;
raise(struct emp, double);
…..
printf(―senas atlyginimas: %.2f\n‖,
prgmr.salary);
prgmr=raise(prgmr, 0.12);
printf(―naujas atlyginimas: %.2f\n‖,
prgmr.salary);
}
struct emp raise(struct emp person,
double increase) {
person.salary*=(1+increase);
return person;
}
- 37 -
7.10. Struktūrų inicializavimas
• Struktūros gali būti inicializuojamos aprašymo metu
• Struktūros laukams priskiriamos reikšmės pateikiamos kaip sąrašas, atskiriamos kableliais ir apskliaudţiamos
riestiniais skliausteliais
• Neinicializuoti struktūrų elementai pagal nutylėjimą lygūs 0
• Neinicializuoti automatinių struktūrų elementai pagal nutylėjimą nėra apibrėţti
struct course {
char name[30];
int number;
char nickname[30]; } title = { ―c for programmers‖, 1001, ―my guide‖ };
struct mailinfo {
char name[25];
char mail_addr[30]; } proj_member[]= {
{―jean griffin‖, ― first 25‖ }
{―sue armani‖, ―second 13‖ }
{ ―frank kruk‖, ―boulevard 6‖ }
};
int f(void) {
static struct mailinfo admin=
{―administartor‖, ―computer 7‖ };
}
7.11. Struktūros su struktūromis
• Struktūros lauku gali būti struktūra
• Struktūros elementu gali būti rodiklis į jos pačios tipą
• Naudojama susietiems sąrašams ir medţiams
• Susietas sąrašas:
struct info { int num;
float sum;
struct info *next; };
• Medis:
struct node { int key;
char description[50];
struct node *left, *right; };
7.12. Apjungimai
• Visi struktūrų aprašymo ir panaudojimo aspektai (tegai, šablonai, priėjimas), kurie buvo aprašyti kalbant apie
struktūras, tinka ir apjungimams.
• Vienintelis skirtumas – naudojamas ne raktinis ţodis struct, bet raktinis ţodis union.
• Lietuviškai kartais vadinama apjungimais, kartais vadinama sąjungomis
• Skirtingai negu struktūrose, apjungimas turi tik vieną iš savo elementų kiekvienu laiko momentu.
• Apjungimas panaudoja vieną atminties sritį skirtingų kintamųjų saugojimui skirtingais laiko momentais
• Faktiškai apjungimas – tai struktūra, kurios visi elementai pradedami su postūmiu lygiu 0 ir skirtingais momentais
konceptualiai persidengia.
7.13. Apjungimų palyginimas su struktūromis
• Aprašymas, tegas, šablonai – viskas taip, kaip ir su struktūromis.
• Aprašymui naudojamas raktinis ţodis union
• Duotu laiko momentu saugo tik vieną elementą
• Pakankamai didelis, kad saugotų didţiausią galimą elementą
struct s_tag { union u_tag {
char c; char c;
int i; int i;
double d; double d;
} s_item; } u_item;
printf(―s_tem dydis %d\n‖,sizeof(s_item));
printf(―u_tem dydis %d\n‖,sizeof(u_item));
- 38 -
7.14. Apjungimų pranašumai
• Apjungimai neretai įtraukiami į struktūras, o vienas iš struktūros elementų naudojamas kaip raktas, kad parodyti kuris
iš apjungimo elementų bus naudojamas
• Lankstumas – gali saugoti skirtingo tipo objektus atmintyje
• Daţnai naudojama, kad taupyti atmintį
struct mail {
char id; // a- active, r – retired
union { struct { char name[30];
char dept[10];
char location[3]; } active;
struct { char name[30];
char street[20];
char city_code[5]; } retired; } info; } person;
7.15. Išvardijimai
• Naudojami tuomet, kada reikia konstantų sekos ir nenorime sukurti naujo duomenų tipo.
• Įvedami išvardinti tipai (enumerated types)
• Aprašomi naudojant raktinį ţodį enum.
• Tarkime mums reikia mėnesių sekos metuose ―masyvo‖:
enum e_months {JAN=1, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC};
typedef enum e_months month;
month currentmonth;
currentmonth = JUN; /* same as currentmonth = 6; */ printf("%d\n", currentmonth);
• Mes išvardijame mėnesius metuose naujame tipe vadinamame month.
• Mes nesukuriame naujo tipo, nes išvardijimai yra paprasti int tipo skaičiai. Todėl printf funkcija naudoja %d, o ne %s.
• Dėmesio, jeigu JAN=1 pasako C kompiliatoriui išvardijimą pradėti nuo 1 vietoj 0.
• Pastaba: Tai bus beveik tas pats , kas:
#define JAN 1
#define FEB 2
#define MAR 3 /* ... etc ... */
7.16. Struktūrų masyvo surūšiavimas
7.16. Kai kurie praktiniai patarimai: komentarai
1) Nerašykite akivaizdţių dalykų:
pvz: /* graţinam SUCCESS reikšmę */
return SUCCESS;
zerocount+; // padidinam zerocount reikšmę vienetu
Tokie komentarai ne padeda, o tik trukdo.
2) Komentuokite funkcijas ir globalinius kintamuosius. Kadangi globaliniai kintamieji pasirodo įvairiose programos
vietose, patartina kartais priminti jų reikšmę ir paskirtį. Prie funkcijos kūno visada patartina pateikti jos trumpą
charakteristiką. Jei funkcija sudėtinga, patartina nurodyti šaltinį, kuriame aprašytas algoritmas.
#includ <string.h>
#include <stdlib.h>
void main() {
const int l_name=30, l_year=5,
l_pay=10, l_buf=l_name+l_year+l_pay;
struct Man { int birth_year;
char name[l_name+1];
float pay; };
const int l_dbase=100;
Man dbase[l_dbase];
char buf[l_buf+1];
int n_record=1, i=0, j;
get_from_file(dbase, l_dbase);
for(i=0;i<n_record-1; i++) {
imin=i;
for(j=i+1;j<n_record;j++)
if(dbase[j].birth_year<dbase[imin].birth.year) imin=j;
Man a=dbase[i];
dbase[i]=dbase[imin];
dbase[imin]=a; }
return; }
- 39 -
3) Nekomentuokite blogo kodo, o perrašykite tą kodą kartais nepavykus parašyti kodo fragmento sėkmingai, jis
paliekamas, tik uţkomentuojamas. Tuo tarpu komentarai paliekami, ir daţnai nebeaišku kam tie komentarai priklauso.
4) Neprieštaraukite kodui: paprastai parašius programą, komentarai atitinką kodo prasmę, tačiau vėliau padarius kokias
nors modifikacijas, kodas pataisomas, o komentarai paliekami.
5) Komentarai turi įvesti aiškumą ne sumaištį.
7.16. Kai kurie patarimai: vieningas stilius ir idiomos
• Būkite nuoseklūs naudodami atitraukimus ir figūrinius (riestinius) skliaustelius
• Naudokite idiomas vieningam stiliui palaikyti: yra tam tikri stiliai – idiomos, kurie yra priimtini ir naudojami, nors
yra teisingi ir kiti
Galima:
a) i=0;
while(i<=n-1) array[i]=1.0;
b) for(i=0;i<n;) array[i++]=1.0;
c) for(i=n;--i.=0;) array[i]=1.0;
Tačiau naudojamas toks uţrašas:
for(i=0;i<n;i++) array[i]=1.0;
• Begaliniam ciklui naudojamos dvi konstrukcijos ir nereikia naudoti jokių kitų:
for(;;) arba while(1)
• Naudokite else-if grandines daugiašakėms sąlygoms
if(condition1)
expression1
else if(condition2)
expression2
.......
else
default_expression
7.17. Kai kurie patarimai: paslaptingi skaičiai
• Tai konstantos, masyvų dydţiai, simbolių pozicijos ir kitos skaitinės reikšmės
• Tokiems skaičiams suteikite pavadinimus: pvz. enum e={ MINROW=1, MINCOL=1, MAXROW=24, MAXCOL=80,
WIDTH=MAXCOL-1}
• Naudokite simbolines, bet ne sveikas konstantes:
Negerai naudoti if(c>=65 && c<=90)
Geriau naudoti: if(c>=‗A‘ && c<=‗Z‘)
Dar geriau: if(isupper(c))
Objekto dydţio nurodymui naudokite programavimo kalbos priemones:
#define NELEMS(array) (sizeof(array)/sizeof(array[0]))
- 40 -
8. VIII-OJI PASKAITA
Objektinio programavimo principai
8.1. Objektinis programavimas
• Objektiškai orientuoto (objektinio) projektavimo principai
• Sąvokų įsisavinimas: klasė, objektas, sąryšis
• Objektinio programavimo instrumentų panaudojimas
• Projekto, sukurto naudojant objektinius principus, kokybės įvertinimas
• Tinkamo modelio objektiniam projektui parinkimas
• Objektinio projekto dokumentavimas (aprašymas)
• Objektinio projekto gyvavimo ciklas
8.2. C ir C++ kalbos
• Sutinkami daţnai terminai – C ir C++. Tai ta pati kalba, ar dvi skirtingos kalbos ?
• C – tai įprastinė procedūrinė (kartais vadinam struktūrinė) aukšto lygio programavimo kalba. Giminiškos kalbos
Pascal, Fortran, PL/1 ir t.t.
• C++ - tai objektiškai orientuota programavimo kalba. Giminiškos kalbos – Delphi, Smalltalk, šiek tiek Java ir t.t.
• C++ sukurta panaudojant C kalbos sintaksė, leksemas ir visa iki tol sukaupta tos kalbos patirtį.
• Prie klasikinių C kalbos savybių C++ buvo prijungtos objektinio programavimo galimybės
• Teisinga teigti, kad C – tai C++ poaibis, realizuojantis procedūrinio programavimo principus
8.3. Kam reikalingas objektinis programavimas ?
• Visų pirma naudojamas dideliuose projektuose
• Labiausiai nauda juntama, kai programinį produktą kuria dideli ţmonių kolektyvai
• Daugumą šiuolaikinių programų kuria būtent dideli programuotojų kolektyvai
• Tačiau pakanka ir dviejų programuotojų kad nauda būtų akivaizdţiai juntama
• Kolektyvinio programavimo principas – skirtingi asmenys būna atsakingi uţ skirtingas projekto dalis ir reikia, kad jų
dalys būtų tarpusavyje suderintos
• Objektinis programavimas leidţia programą rašyti objektais (realizuoti atskirais objektais), kurie su kitais objektais
bendrauja per tam tikrus interfeisus
8.4. Objektinio programavimo principai
• Kuriant objektinio programavimo principus, stengtasi įvertinti ţmogiško mąstymo principus
• Ţmonės daţnai skirsto stebimus objektus į klases, tos pačios klasės objektai turi bendrų savybių; bendresnės klasės
objektus galima skirstyti į smulkesnių klasių objektus; smulkesnių klasių objektai paprastai paveldi stambesnių klasių
objektų savybes ir įgyja naujų savybių
• Skirtingų klasių objektai turi bendras savybes, bet jos būna išreikštos per skirtingo tipo parametrus.
• Panašūs principai buvo realizuoti, kuriant objektinio programavimo principus.
8.5. Objektinio programavimo procesas
• Objektinio programavimo stilius projektuojant – tai iteracinis aplikacijos vystymas
• Faktiškai tai priešingybė ―kulinarinių receptų‖ stiliui
• Projektuojant objektiniu principu reikia atlikti sekančius veiksmus:
Išorinių reikalavimų nustatymas
Esybių identifikavimas
Jų savybių ir elgesio identifikavimas
Esminių ryšių ir savybių identifikavimas
Iteracijos baigiamos, kai visas projektas tampa pinu ir korektišku
8.6. Išorinis aprašymas
• Sudaromas aukšto lygio koncepcinis projektuojamo objekto modelis
• Išorinis aprašymas:
Sąskaitų tipai, kuriuos padoroja bankas
- 41 -
1. Komercinės sąskaitos (max 7 procentai palūkanų priklausomai nuo kitų sąlygų)
2. Nekomercinės sąskaitos (palūkanos nemokamos, min indėlio dydis 200)
Finansinių operacijų tipai:
1. Indėlio atidarymas, uţdarymas, pakeitimas
2. Finansinės ataskaitos sudarymas
Bankomato funkcijos:
1. Kortelės tikrumo patikrinimas
2. Sąskaitos būklės patikrinimas
3. Finansinių operacijų vykdymas
8.7. Esybių identifikavimas
• Ţodyno sudarymas:
Reikalavimų analizė
Bendravimas su ekspertais
• Esminių abstrakcijų nustatymas, kurios gali būti potencialių pakitimų šaltiniais
Procedūrų abstrakcijos
Duomenų abstrakcijos
• Esybės, kurios ţymimos daiktavardţiais:
gali būti realios arba irrealios
Gali būti specialiai išrastos
Ne visos priskiriamos konkrečioms klasėms
Bendru atveju yra kandidatais konkrečiai klasei
Esybių identifikavimo pavyzdys
• Procedūros:
• Visų finansinių deklaracijų spausdinimas
• Objektai:
• Komercinės sąskaitos
• Nekomercinės sąskaitos
8.8. Esybių elgesio identifikavimas
• Išnagrinėti kiekvieną esybę atskirai nuo kitų
• Identifikuoti duotos esybės elgesį
• Esybės ribos gali būti pakeistos dėl:
Kai kurių esybių didelio sudėtingumo
Esybių apjungimo
Kitų esybių reikšmių pasikeitimo
• Esybių elgesio identifikavimas – tai visada iteracinis procesas
• Esybių identifikavimo stadija kaip taisyklė sudėtingesnė, negu esybių aibės nustatymo stadija
8.9. Esybių požymių nustatymas
• Sumaţinti abstrahavimo lygį
• Išnagrinėti kiekvieną esybę iš išorės
• Nustatyti poţymius
• Nustatyti naujas esybes
• Objektiškai orientuoto projektavimo principas nepalaiko grynųjų ―iš viršaus – į apačią‖ arba ―iš apačios – į viršų‖
projektavimo strategijų
• Nustatant poţymius daţnai tena grįţti į ankstesnes stadijas
• Nustatant poţymius galimos projektavimo proceso iteracijos
8.10. Ryšių identifikavimas
• Objektų integravimo į sistemą būdų nustatymas:
- bendrumų, panašumų nustatymas
- statinės ir dinaminės semantikos nustatymas
- matomumo nustatymas
Ryšių identifikavimo metodai:
Iš apačios į viršų
Iš viršaus į apačią
Kombinuotas
- 42 -
8.11. Objektinio projektavimo būdo apibendrinimas
• OOP procesas yra iteracinis, jame realizuota mišri projektavimo strategija
• OOP palaiko projektuotojo darbą keliuose projekto abstrahavimo lygiuose
• Objektinis projektavimas - tai ―kulinarinių receptų knygos antitezė
• Pagrindiniai OOP etapai:
o išorinis projektavimas ir reikalavimų sistemai nustatymas
o Esybių identifikavimas
o Esybių elgesio nustatymas, jų poţymių ir charakteristikų aptikimas
o Ryšių ir struktūrų identifikavimas
• OOP procesas sustabdomas po uţbaigto ir korektiško projekto sugeneravimo
8.12. Objekto sąvokos įsisavinimas
• Objektas yra tam tikros objektų klasės realizacija
• Objektas gali turėti būseną
• Objektas gali turėti elgseną
• Objektas gali turėti pavadinimą arba likti bevardis
• Objektas – tai tam tikros klasės egzempliorius
• Objekto matomumas yra apibotas kitų objektų atţvilgiu
• Objektas gali būti nagrinėjamas specifikavimo ir realizavimo poţiūriu
8.13. Objekto būsena
8.14. Objektų rolės
• Objektai gali funkcionuoti keliuose reţimuose:
• Operatoriaus reţimas: Operatorius veikia į kitus objektus ir nepatiria poveikio iš jų pusės. Bankinis automatas gali
būti vertinamas kaip kortelių apdorojimo interfeisas
• Serverio reţimas. Serveris – tai toks objektas, kuris patiria išorinius poveikius ir nedaro poveikio kitiems objektams.
• Tarpininko reţimas. Tarpininkas – ta objektas, kuris priima kitų objektų poveikius ir pats daro poveikius kitiems
objektams.
• Didelę įtaką turi poveikių seka
8.15. Objektų elgesys
• Visiškai izoliuotų objektų neegzistuoja. Objektai priima poveikius ir perduoda poveikius kitiems objektams
• Vartotojo poţiūriu galima išskirti penkias poveikių klases:
• Modifikatoriai – operaotoriai, kurie pakeičia objekto būseną
• Selektoriai – operatoriai, kurie įgalina prieiti prie objekto būsenos
• Iteratoriai - leidţia prieiti prie visų objekto dalių grieţtai apibrėţta tvarka
• Konstruktoriai – operatoriai, kurie inicializuoja objektą
• Destruktoriai – operatoriai, kurie sunaikina objektą.
• Konstruktoriai ir destruktoriai C++ kalboje sukuriami automatiškai
class ATM {
public:
ATM(int serial_num);
~ATM();
void login();
void password();
void whichtransaction();
void requestwithdraw();
void requestdeposit();
void requesttransfer();
void logout();
private:
const int serialNumer;
enum events {CARD,PIN,TRANSACTION,WITHDRAW, DEPOSIT,
TRANSFER, ANOTHER} event;
enum state {LOGIN, LOGOUT, IDENTIFY_TRANSACTION,
PROCESSING } state;
void (ATM::*functiobns) {SIZE]{EVENT]();
MoneyDispatcehr md;
ReceiptDispatcehr rd;
Cardslot cs;
};
- 43 -
8.16. Klasės
• Klasė:
• Tai abstrakti sąvoka (duomenų tipas)
• Apibrėţiama objektų, kurie turi panašią elgseną ir panašią struktūrą, aibei
• Interfeisas aprašo išorines klasės savybes. Interfeiso elementai visada turi modifikatorių public. Interfeiso elementais
gali būti:
• - operatoriai
• - klasių aprašymai
• - konstantės
• -kintamieji
• Realizacija parašo klasės sandarą. Realizacijos elementai turi modifikatorių private. Realizacijos elementai – tai:
• - operatoriai
• - klasių aprašymai
• - konstantės
• - kintamieji
• Klasė aprašoma naudojant raktinį ţodį class
• Konkreti klasės realizacija vadinama objektu
8.17. Klasių ir objektų ryšiai
• Klasių ryšiai
• - IS-A (ryšio tipas ―yra)
• - HAS –A (ryšio tipas ―turi‖)
• - USES – A (ryšio tipas ―naudoja‖)
• - LIKE – A (ryšio tipas ―panašus‖)
• - CREATE – A (ryšio tipas ―sukuria)
Objektų ryšiai:
- HAS - A
- USES - A
8.18. Trys objektinio programavimo banginiai
• Daţnai teigiama, kad objektinis programavimas remiasi trimis banginiais (trimis pagrindiniais principais):
• - inkapsuliacija
• - paveldėjimu
• - polimorfizmu
• Inkapsuliacija – tai duomenų kintamųjų) ir jų apdorojimo procedūrų susiejimas į vieną objektą. C++ kalboje
inkapsuliacijos principą realizuoja klasės. Klasės susieja kintamuosius (duomenis) ir funkcijas (procedūras)
veiksmams su jais į vieną objektą. C++ terminologijoje funkcijos, priklausančios klasėms, vadinamos metodais
8.19. Klasės
• C kalboje duomenys ir funkcijos apibrėţiami atskirai. Toks atskyrimas apsunkina programų struktūrinį skaidymą.
• C++ įvesta nauja sąvoka – klasė. Klasė leidţia apjungti duomenis ir struktūras, kurios operuoja su jais, į vieną
struktūrą. Toks apjungimas vadinamas duomenų inkapsuliacija. Inkapsuliacija leidţia paslėpti konkrečią klasės
realizaciją, palengvinant programų derinimą ir modifikavimą.
• Klasės apibrėţimas:
class [<tag>] {
< member – list>
} <declarators>
• < tag> - klasės pavadinimas
• <member-list> - klasės narių sąrašas. Funkcijos, priklausančios klasei, vadinamo funkcijomis- elementais arba
metodais.
• <declarators> - vieno arba daugaiu tos klass objektų apibrėţimai
- 44 -
• Raktinis ţodelis this – tai rodiklis į einamą klasės objektą. Rodiklis this yra pastovus dydis ir programoje negali būti
keičiamas.
• Aprašius klasę ir sukūrus tos klasės objektus, jais galima manipuliuoti. Kai kuriuos duomenis ir metodus galima
padaryti nematomais uţ klasės ribų
8.20. Klasei priklausančių elementų matomumas
• Priėjimo prie klasės elementų valdymui naudojami trys operatoriai: public, private ir protect.
• Public tipo elementai prieinami visiems klasės metodams ir kitų klasių objektams
• Private tipo elementai prieinami tik tos klasės metodams
• Protected tipo elementai prienami visiems tos klasės metodams ir kitų klasių objektams, bet jų prieinamumas
neperduodamas paveldėjimo metu
• Šie raktiniai ţodţiai aprašant klasę gali būti pateikti bet kuria tvarka, po jo turi būti dvitaškis ir visi po to aprašyti
klasės elementai yra nurodyto tipo
Klasės aprašymo pavyzdys
8.21. Klasei priklausantys metodai
• Jeigu metodo kūnas yra trumpas, toks metodas daţniausiai apibrėţiamas klasės viduje. Taip pat galima vietoj
iškvietimo naudoti metodo kūno įstatymą, naudojant raktinį ţodį inline
• Pvz:
class Line {
public:
void setlength(int newLength) { length= newLength; }
int getLength() { return length; }
private:
int length; }
• Jeigu metodo išeities tekstas nėra toks trumpas, tuomet jis uţduodamas uţ klasės ribų. Taip uţduodant metodą prieš
jo pavadinimą turi eiti klasės vardas ir matomumo srities operatorius ::
• Pvz:
void convert:: ConvertString(void) {
int i;
for(i=0; sText[i]!=„\0‟;i++) {
sText[i]=tolower(sText[i]); }
return i; }
8.22. Klasei priklausančių metodų iškvietimas
• Metodų iškvietimas:
klasės_pavadinimas.metodo_pavadinimas(argumentų sąrašas); klasės_pavadinimas ->
metodo_pavadinimas(argumentų sąrašas);
Ką primena tokia metodų iškvietimo procedūra ?
Jeigu metodas iškviečiamas iš kito tos pačios klasės metodo, tuomet klasės pavadinimo nurodyti neprivaloma.
Pvz: class A { void fun1();
void fun2(); }
A.fun1();
class Sample {
int x;
void Load();
public:
void setStr();
void getStr();
char sDataText[80];
private:
char snameText[80];
int iIndex;
public:
void convertStr();
int iLevel();
};
- 45 -
8.23. Metodai, nekeičiantys klasės objekto
• Jeigu metodas nekeičia objekto, kuriam yra iškviečiamas, tuomet jį galima aprašyti su raktiniu ţodeliu const.
• Tokie metodai negali keisti klasės elementų arba kveisti kitus metodus, apibrėţtus be raktinio ţodţio const.
• Pvz: class ClassMem {
• public:
• void setWeight(int newWeight);
• int getWeight() const;
• private:
• int weight; }
8.24. Statiniai metodai
• Kai kuriuos klasės elementus galima apibrėţti statiniais, naudojant raktinį ţodį static.
• Jiems taikomi apribojimai:
• - statiniai metodai gali tiesiogiai kreiptis tik į statinius klasės narius;
• - statinis metodas negali būti paskelbtas virtualiu metodu
• Negalima apibrėţti statinio metodo su tuo pačiu pavadinimu ir tuo pačiu parametrų rinkiniu kaip ir statinį klasės
metodą
• Statinių metodų ypatybė – juos galima iškviesti nesukuriant klasės objektų
• Pvz:
class Circle {
public:
static void GetPi() { return fPI; }
private:
static float fPi; };
float fNumber;
fNumber = Circle::getPi();
- 46 -
9. IX-OJI PASKAITA
Naujos C++ savybės. Objektų klasifikavimas.
9.1. Naujos C++ savybės
• C++ kalboje įvesta keletas naujų savybių, kurių neturi C programavimo kalba.
• Šios savybės nėra objektinio programavimo dalis, tiesiog dalinis C kalbos patobulinimas
• Pvz. komentarų tipas // pirmiausia buvo įvestas C++ kalboje, tačiau vėliau sėkmingai perkeliavo į C kalbą
• Čia kalbėsime apie panašias C++ kalbos modifikacijas
• Jų nėra C programavimo kalboje, todėl nesistenkite jų ir taikyti rašydami C programas
9.2. Įvedimas/ išvedimas
• C++ įvesti teksto įvedimo iš klaviatūros arba teksto išvedimo į ekraną modifikatoriai konsoliniame reţime:
• cout << - teksto išvedimas į ekraną
• cin >> - teksto įvedimas iš klaviatūros
• int iNum;
• cout << “iveskite sveika skaiciu”;
• Cin<<iNum;
• cout>> “jus ivedete skaiciu”>>iNum>>”\n”;
• Reikia panaudoti header failą iostream.h, t.y. , į programą įtraukti eilutę
• #include <iostream.h>
9.3. Konstantos
• Jei standartinėje C kalboje apibrėţti pastoviems dydţiams reikia naudoti priešprocesoriaus direktyvą #define, tai C++
įvestas raktinis ţodis const.
• Jo pranašumas prieš direktyvą tai, kad kompiliatorius visada patikrina jo tipą
• Pvz: const int max_number=256;
• Paţymėtina, kad C++ const tipo objektas gali būti pakeistas:
• Pvz: int iNumber;
int *const ptrNumber = &iNumber;
9.4. Nuorodos
• C++ galima apibrėţti nuorodą į objektą-kintamąjį arba klasės objektą.
• Nuorodai sukurti naudojamas operatorius &
• Nuoroda saugo objekto adresą, bet ją galima naudoti ir kaip patį objektą
9.5. Kintamųjų aprašymas
• C kalboje reikalaujama visus lokalinius kintamuosius apibrėţti funkcijos pradţioje, t.y. iki pirmojo toje funkcijoje
atliekamo operatoriaus.
• C++ šis reikalavimas suprastintas: kintamasis turi būti aprašytas iki pirmo duoto kintamojo panaudojimo programoje
• Pvz:
void main() {
int a, b, c;
a=5; b=7;
c=a+b;
for (int i=0, int sum=0;i<10;i++) sum+=i;
}
9.6. Atminties paskirstymas
• Standartinė C kompiliatoriaus biblioteka turi specialias funkcijas (malloc, free) atminties valdymui (rezervavimui,
atlaisvinimui).
• C++ kalboje atminties valdymui įtraukti specialūs operatoriai atminties valdymui – new ir delete
• new operatorius sukuria uţduoto tipo objektą
• Operatoriaus new formatas:
- 47 -
• new type_name [initializer]
new (type_name) [initializer];
Initializer sako, kad sukurtam objektui galima priskirti pradinę reikšmę
Pvz: char *litera;
int *pi;
litera= new char;
pi=new int(3.1415);
Kad atlaisvinti anksčiau uţrezervuotą atmintį operatoriumi new, naudojamas operatorius delete
pvz: delete pointer;
Operatorius new leidţia sukurti bei atlaisvinti ir dinaminius masyvus:
pvz:
long *pdatažnew long[100];
delete [] pointer;
9.7. Funkcijų pavadinimų perkrovimas
• C++ leidţia sukurti ir naudoti keletą funkcijų su vienodais pavadinimais, bet skirtingu parametrų rinkiniu ir skirtingu
funkcijos turiniu.
• C variante toks elgesys negalimas
• Pvz: int Square(int a, int b);
int Square (int a);
int Square(nt a, int b) { return (a*b); }
int Square(int a) { return(a*a); }
9.8. Funkcijos parametrų uždavimas pagal nutylėjimą
• C++ galima uţduoti funkcijos argumentus pagal nutylėjimą:
• Pvz: int Summa(int first, int second, int third=0, int fourth=0) {
return(first+second+third+fourth); }
void main() {
int value1=10, value2=20, value3=30, value4=40;
int result;
result=Summa(value1, value2, value3, value4);
result=Summa(value1, value2, value3);
result=Summa(value1, value2);
9.9. Abstrakcijų klasifikavimas
• Esybių grupavimas klasėse gali būti atliekamas atsiţvelgiant į:
• - apibendrinimą
• - specializavimą
• - agregavimą
• Klasifikavimo procesas apima savyje:
• - esminių abstrakcijų identifikavimą (sąskaitos, indėliai, depozitai)
• - abstrakcijų išradimą (duomenų bazė, terminalo ekranas, banko valdytojas)
• Klasifikavimo procesas visada yra reliatyvus ir priklauso nuo kategorizacijos tikslų
9.10. Skirstymas į klases priklauso nuo klasifikavimo tikslų
• Klasė ―katė‖ savininko poţiūriu:
class Domestic {
public:
virtual void pet();
virtual void feed();
virtual void brush();
private: };
class Cat: public Domestic {
public:
void pet();
void feed();
void brush();
private:
};
• Klasė ―katė‖ veterinaro poţiūriu:
class Patient {
public:
virtual void mendLegs();
virtual void fix();
};
class Cat: public Patient {
public:
void mendLegs()l;
void fix();
private:
}
- 48 -
9.11. Klasifikavimo šablonai
9.12. Mechanizmų klasifikacija
• Mechanizmas – tai suderinta objektų klasių sąveika, tenkinanti tam tikrus išorinius reikalavimus
• Specifinės sudėtingų, painių objektų savybės išreiškiamos per priklausomybę nuo projektinių sprendimų
• Mechanizmų klasifikacija atliekama per:
- Esminių mechanizmų identifikaciją
- Naujų mechanizmų išradimą
9.13. Konstruktoriai ir destruktoriai
• Daţnai sukuriant objektą, reikia atlikti pradinę objekto inicializavimą, pvz. išskirti atminties sritis masyvams,
priskirti kintamiesiems pradines reikšmes.
• Objektą sunaikinant atminties sritis reikia atlaisvinti
• C++ tuo tikslu yra sukurt specialūs metodai, vadinami konstruktoriais ir destruktoriais
• Funkcija-konstruktorius visada turi ta patį pavadinimą, kaip ir klasė. Ji leidţia atlikti objekto inicializavimą jo
sukūrimo metu
• Funkcija-destruktorius taip pat turi tą patį pavadinimą kaip ir klasė, bet prieš pavadinimą visada dedamas tildės ~
ţenklas. Ji atlieka objekto sunaikinimą.
9.14. Bendri klasės objektų nariai
• Kai kada yra patogu, kad visi duotos klasės objektai turėtų bendrus duomenų elementus, kurie ir yra naudojami
bendrai.
• Bendri klasės duomenų elementai apibrėţiami naudojant raktinį ţodį static
• Pvz: class CWindow {
public:
int xLeftTop, xRightBottom;
int yLeftTop, yRightBottom;
static char title[80];
void SetTitle(char *); }
char CWindow::title[80]=“lango pavadinimas”;
Kiekvienas klasės CWindow objektas turės unikalias lango koordinates ir tą patį lango pavadinimą
9.15. Draugiškos klasės ir draugiškos funkcijos
• C++ galima uţduoti draugišką klase funkciją, naudojant raktinį ţodį friend.
• Aprašant klasę nurodomas tiktai funkcijos pavadinimas.
• Paprastai gali būti
klasifikuojami:
• Realūs daiktai
• Rolės
• Įvykiai
• Sąveikos
• Specifikacijos
• Ţmonės, daiktai, būstai
• organizacijos
• Koncepcijos
• Struktūros
• Kitos sistemos
• Įrenginiai
• Daiktavardţiai,
veiksmaţodţiai (reikia būti
atsargiems)
#include <iostream.h>
class CRectangle {
int *width, *height;
public: CRectangle (int,int);
~CRectangle ();
int area (void) {
return (*width * *height);} };
CRectangle::CRectangle (int a, int b) {
width = new int;
height = new int;
*width = a; *height = b; }
CRectangle::~CRectangle () {
delete width;
delete height; }
int main () {
CRectangle rect (3,4), rectb (5,6);
cout << "rect area: " << rect.area() <<
endl; cout << "rectb area: " <<
rectb.area() << endl;
return 0; }
- 49 -
• Draugiška funkcija nėra klasės elementas, bet ji gali prieiti prie visų klasės elementų, įskaitant ir private bei protected
elementus
class point {
public:
friend void point:::Clear(point *);
private:
int m_x;
int m_y; };
void Clear(point *ptr) {
ptrPoint -> m_x=0;
ptrPouint -> m_y=0;
return;
}
• Analogiškai galima uţduoti ir draugiškas klases:
class point {
...............
};
class line {
public:
friend class point; // klasė pouint skelbiama
// draugiška klasei line
}
9.16. Paveldėjimas
• Prisiminkime tris bazinius objektinio programavimo principus: inkapsuliaciją, paveldėjimą ir polimorfizmą
• Paveldėjimas – antrasis iš šių bazinių principų
• Iš kur kilo idėja panaudoti paveldėjimą ? Be abejo iš gamtos
• Ką gamtoje reiškia paveldėjimas ?
• Objektiniame programavime paveldėjimas reiškia beveik tą patį, ką ir gamtoje
• Motininės klasės perduoda savo savybes dukterinėms klasėms
• Daţniausiai naudojami pavadinimai: bazinė klasė ir išvestinė klasė; kartais naudojamos sąvokos – motininė klasė ir
dukterinė klasė
• Iš anksčiau išvestų klasių galima išvedinėti ir naujas išvestines klases
• Išvestinė klasė apima savyje visus bazinės klasės elementus ir metodus, bei gali juos papildyti nuosavais elementais ir
metodais
• Išvestinė klasė pati gali tapti bazine klase kitoms išvestinėms klasėms. Tokiu atveju išvestinė klasė paveldės visų
bazinių klasių elementus ir metodus
9.17. Vienetinis paveldėjimas
• Vienetinio paveldėjimo atveju išvestinė klasė paveldi elementus tik iš vienos bazinės klasės
• Paveldėjimas apibrėţiamas taip:
class [<tag>] [: <base> ] ] {
<member_list>
} [<declarators>]
Angliškoje literatūroje paveldėjimas vadinamas inheritance
Sukurtoje klasėje galima apibrėţti elementus, kurių vadai sutampa su bazinės lasės elementų vardais
• Vienetinio paveldėjimo pavyzdys:
class Base {
// klasės Base elementai
};
classs DerivedFirst: Base {
// klasės DerivedFirst elementai
};
class DerivedSecond : Base {
// klasės DerivedSecond elementai
};
- 50 -
9.20. Daugybinis paveldėjimas
• Daugybinis paveldėjimas atliekamas paveldėjimas. Išvestinė
klasė šiuo atveju gali turėti keletą bazinių klasių panašiai
kaip vienetinis
• Vietoj vienos bazinės klasės pavadinimo, nurodomas bazinių
klasių pavadinimų sąrašas
• Apibrėţimas:
• class [<tag> [: [<base_list]] {
<member_list>
} [<declarators>]
• Daugybinio paveldėjimo parašymo pavyzdys:
class BaseFirst {
// klasės BaseFirst elementai
};
class BaseSecond {
// lasės BaseSecod elementai
}
class Derived: BaseFirst, BaseSecond {
// klasės Derived nuosavi elementai
}
9.21. Priėjimo prie bazinės klasės elementų apribojimas
• Priėjimą prie klasės elementų galima valdyti naudojant priėjimo modifikatorius
• Kuomet paveldima bazinė klasė, priėjimą galima valdyti prie bazinės klasės elementų
• Tokiu atveju yra svarbu su kokiais specifikatoriais yra aprašyti bazinės klasės elementai
9.22. Bazinės klasės metodų perdefinavimas
• Išvestinėje klasėje galima apibrėţti metodus ir duomenų elementus tais pačiais vardais, kurie jau buvo panaudoti
bazinėje klasėje
• Tokiu atveju tie bazinės klasės elementai tampa paslėptais
• Norint operuoti su paslėptais elementais, reikia naudoti pilną vardą:
bazinės_klasės_vardas:: elemento_vardas
Pvz:
class C: A,B {
a=A::a
}
9.23. Daugybinio paveldėjimopavyzdys
Bazinės klasės
elemento
specifikatorius
Bazinės klasės priėjimo specifikatorius
public protected private
public public protected private
protected protected protected private
private - - -
Derived
Class
Derived
ClassOne
Derived
ClassSecond
Base
ClaassOne
BaseClassSecond
BaseClass
• #include <iostream.h>
class CPolygon { protected:
Int width, height;
public: void set_values (int a, int b) {
width=a; height=b;} };
class COutput { public:
void output (int i); };
void COutput::output (int i) {
cout << i << endl; }
class CRectangle: public CPolygon, public
COutput { public:
int area (void) { return (width * height); } };
class CTriangle:
public CPolygon, public COutput { public: int
area (void) {
return (width * height / 2); } };
int main () { CRectangle rect; CTriangle trgl;
rect.set_values (4,5); trgl.set_values (4,5);
rect.output (rect.area()); trgl.output (trgl.area());
return 0; }
- 51 -
9.24. Rodikliai į klases
• C++ leidţiama apibrėţti rodiklius į klases.
• Apibrėţta klasės tampa veikiančiu duomenų tipu
• Apibrėţti rodiklį į klasę paprasčiausiai reikia panaudoti klasės pavadinimą ir rodiklio ţenklą
• CRectangle * prect; //is a pointer to an object of class
• // CRectangle.
Kaip ir su kitais agreguotais duomenų tipais, norint prieiti prie rodiklių į klases elementų, reikia naudoti ţenklą ->
#include <iostream.h>
class CRectangle {
int width, height;
public: void set_values (int, int);
int area (void) {
return (width * height);} };
void CRectangle::set_values (int a, int b) {
width = a; height = b; }
int main () {
CRectangle a, *b, *c;
CRectangle * d = new CRectangle[2]; b= new
CRectangle;
c= &a;
a.set_values (1,2);
b->set_values (3,4);
d->set_values (5,6);
d[1].set_values (7,8);
cout << "a area: " << a.area() << endl;
cout << "*b area: " << b->area() << endl;
cout << "*c area: " << c->area() << endl;
cout << "d[0] area: " << d[0].area() << endl;
cout << "d[1] area: " << d[1].area() << endl;
return 0; }
- 52 -
10. X-OJI PASKAITA
Paveldėjimas. Operatorių perkrovimas.
10.1. Išvestinės klasės
• Sudaro prielaidas sukūrimui naujos klasės, kuri yra anksčiau sukurtos klasės variacija
• Išvestinė klasė gali prieiti prie bazinės klasės atvirų ir uţdarų narių
• Išvestinėje klasėje nustatomi private, protected ir public bazinės klasės narių apribojimai
• Pagal nutylėjimą nustatomi private apribojimai
• Pilnai atitinka įprastinės klasės apribojimus, bet papildomai turi bazinės klasės elementus su nuosavais priėjimo
apribojimais
10.2. Išvestinių ir bazinių klasių elementai
• Grafiškai elementų apribojimai gali būti pavaizduoti taip:
• Private Base Derived
private ------ private
protected ---- private
public -------- private
• Protected
private ------- private
protected ---- protected
public -------- protected
• Public
private -------- private
protected ----- protected
public ---------- public
10.3. Išvestinės klasės funkcijos- nariai
• Išvestinėse klasėse gali pasitaikyti funkcijos-nariai, turinčios tuos pačius pavadinimus kaip ir bazinės klasės nariai
• Argumentų kiekis ir jų tipai, o taip pat graţinamos reikšmės tipas gali skirtis nuo bazinės klasės metodo argumentų
skaičiaus, tipai ir graţinamos reikšmės tipas
• class PayPhone: public Phone { // ......
public:
// ......
void GiveDialTone(); };
Phone home(516,555,8858);
PayPhone booth(708,555,5444);
//.....
home.GiveDialTone();
// Phone::GiveDialTone();
booth.GiveDialTone();
// payPhone::GiveDialTone();
• Išvestinės klasės funkcijos-nariai gali kreiptis į bazinės klasės funkcijas-narius.
• Galima kreiptis net į tas funkcijas – narius, kurių pavadinimai sutampa
• Pvz: void PayPhone::GiveDialTone() {
// atli
Phone::GiveDialTone(); // iškviečia bazinės klasės metodą
}
10.4. Priėjimo valdymas
class Phone {
.......
// klasės Phone nariai
}
class PayPhone: public Phone
{
int centsDeposit;
public:
PayPhone(int area, in exchange, int ,line);
Void GiveDialTone();
Int AcceptCoins();
};
// išvestinės klasės objekto apibrėţimas
PayPhone booth(708,555,5444);
• Leidţia paskelbti uţdarais arba atvirais
uţdaros klasės elementus arba atviros klasės
elementus atitinkamai
• Perkrautoms funkcijoms leidţia sugraţinti
pirminį priėjimo apribojimų nustatymą, kurį
jos turėjo bazinėje klasėje
• class Base {
int a;
void g();
public:
int b,c;
void f(), f(int), g(int);
};
class Derived: private Base {
public:
Base::a; // klaida: negalima public
Base b; // b vėl public
int c;
Base c; // negalima c du kartus
Base:: f; // visos f() vėl public
Base::g // negalima
- 53 -
10.5. Virtualios funkcijos-nariai
• Leidţia išrinkti narius su tais pačiais pavadinimais per rodiklį į funkciją, priklausomai nuo objekto tipo, į kurį nurodo
rodiklis, o ne nuo rodiklio tipo
• Leidţia naudoti objektinę programavimo paradigmą
• Argumentų tipai, jų skaičius, o taip pat graţinamos reikšmės tipas, gali būti tokie patys kaip ir to paties pavadinimo
bazinės klasės funkcijoje
• Negali būti statinėmis
• Gali būti naudojamos abstrakčių bazinių klasių sudarymui
• Abstrakti bazinė klasė - tai klasė, turinti bent vieną virtualią funkciją- narį (metodą)
• Pilnai virtuali funkcija - narys - tai virtuali funkcija-narys, kuriai yra deklaruotas interfeisas, o jo realizacija parašyta
išvestinėje klasėje
• Tam reikia metodo apibrėţimą prilyginti nuliui
• Abstrakčios klasės destruktorius visada privalo būti virtualus
• Anstyvosios C++ versijos nepalaikė abstrakčių bazinių klasių
• Negalima apibrėţti abstrakčios klasės objekto. Galima apibrėţti tik išvestinės klasės objektą
• Pilnai virtuali funkcija niekada negali aiškiai arba netiesiogiai iškviesta iš konstruktoriaus
• Iš konstruktoriaus gali būt iškviesta tik per ne virtualią klasės funkciją-narę. Tačiau bus iškviesta duotoje klasėje
apibrėţta funkcija
10.6. Pilnai virtualios funkcijos - nariai
10.7. Virtualios klasės ir kostruktoriai
class Phone {
// ...
public:
virtual void GiveDialTone();
// virtuali funkcija
};
class PayPhone:: public Phone {
int centsDeposited;
public:
PayPhone(int area, int exchange, int
line);
void Give DialTone();
int AcceptCoins(); };
// .....
Phone home(516,555,8858);
PayPhone booth(708,555,5444);
Phone *p;
// rodiklis į bet kurį Phone tipą
p=&home; p->GiveDialTone();
// Phone::GiveDialTone();
p=&booth; p->GiveDialTone(); //
PayPhone::GiveDialTone();
struct Point {
int x,y;
Point(int x0=0, y0-0) {
x=x0; y=y0; }
};
class Shape { // abstrakti bazine klase
protected:
Point center;
public:
Shape(Point c): center(c) { }
virtual Shape& Draw() const=0; };
// pilnai virtuali Shape
class Cicle: public Shape {
int radius;
public:
Circle(Point c, int r=1);
Shape& Draw() const; };
// Draw() bus apibrezta
//……
Shape amorphus; // Klaida: abstrakti
klase
Circle c;
Shape *p=&c; // teisinga
class Phone {
// ....
public:
Phone(int a, int e, nt l);
{ //.....
AcceptDigits(); // si funkcija iskviecia ...
}
virtual void GiveDialTone(); // ... Sia funkcija
long AcceptDigits() {
// ....
GiveDialTone(); // ... kadangi tiesiogonis
iskvietimas yra cia
}
// ....
};
class PayPhone: public Phone {
// ...
public:
// ....
void GiveDialTone(); // si funkcija
negali buti iskviesta
};
- 54 -
10.8. Bazinių klasių konstruktoriai
• Iškviečiami prieš išvestinių klasių konstruktorių iškvietimą
• Turi būti aiškiai nurodyti kiekviename išvestinės klasės apibrėţime, jeigu bazinėje klasėje nėra konstruktoriaus pagal
nutylėjimą
• Bazinės klasės konstruktoriaus pavadinimas ir jo argumentai atskiriami nuo išvestinės klasės konstruktoriaus
pavadinimo dvitaškiu
• Daugybinių bazinių klasių konstruktoriai atskiriami vienas nuo kito dvitaškiais
• Niekada tiesiogiai arba netiesiogiai iškviesti virtualios bazinės klasės metodus
10.9. Bazinių klasių destruktoriai
• Iškviečiami po išvestinių klasių destruktorių
• Gali būti virtualiais. Abstrakčios klasės destruktorius gali būti virtualiu
• Gali per ne virtualias funkcijas- narius tiesiogiai arba netiesiogiai iškviesti virtualius metodus
• Tačiau šiuo atveju iškviečiama funkcijos versija duotai klasei arba bazinei klasei, jeigu tokia egzistuoja, ir niekuomet
išvestinei klasei
10.10. Daugybinės bazinės klasės
• Leidţia išvesti naują klasę daugiau negu iš vienos bazinės klasės
• Bazinių klasių išvardijimo tvarka sąraše neturi įtakos.
• Priėjimas prie bazinių klasių elementų, turinčių vienodus pavadinimus, turi būti atliekamas per bazinių klasių
pavadinimus
class A { public: void f(); };
class B { public: void f(); };
class C: public A, public B { /* .... */ };
// .....
C c;
c.f(); // klaida: kur f ?
c. A:f(); // teisingai
• Patogiausia dviprasmybes spręsti abiejų funkcijų perdengimu išvestinėje klasėje
class C: public A, public B {
// ....
public:
void f() { A::f(); B::f(); }
};
// ...
C c;
c.f(); // dabar teisingai
• Klasėms, kurios yra išvestos iš klasių su bendra baze, pagal nutylėjimą egzistuoja du bendros bazės objekto
egzemplioriai
• Į bendros bazinės klasės elementus galima kreiptis per bet kurios iš išvestinių klasių pavadinimą
10.11. Virtualios bazinės klasės
• Klasėms, kurios yra išvestos iš išvestinių klasių su bendra virtualia bazine klase, egzistuoja tiktai vienas bendros
bazinės klasės elementas
• Apibrėţiami raktinio ţodţio virtual panaudojimu bazinės klasės priėjimo lygio specifikatoriuje
• Prieinant prie bazinės klasės elementų nebereikia nieko nurodyti papildomai
• Jų konstruktoriai iškviečiami pagal paskutinės išvestinės klasės grandinėlę
Class Phone {
// ...
public:
Phone(int, int, int); // turi
argumentus
// .....
};
class PayPhone: public Phone {
// .....
public:
PayPhoneint a, int e, int l):
Phone(a,e,l) {
// .....
}
// .....
};
- 55 -
• Apibrėţiant klasę galima sumaišyti su ne virtualiomis bazinėmis klasėmis
10.12. Šablonai
• Kas yra šablonas ?
• Šablonai naudojami ir objektiniame programavime
• Jie išplečia klasės ir funkcijos supratimą
• Suteikia galimybes parametrizuoti funkcijas ir klases
• Faktiškai įgalina apibrėţti funkcijas ir klases kaip ―bet kurio tipo‖ elementus
• Sąvotiškai leidţia atsisakyti tipų
10.13. Funkcijų šablonai
• Tai funkcijos kaip šablono specifikacijos paskelbimas. Šablono specifikaciją sudaro raktinis ţodis template, paskui
kurį seka parametrų, įtrauktų į < > skliaustelius, sąrašas
• Turi tipo parametrus kurie ţymimi raktiniu ţodţiu class, po kurio seka identifikatorius. Identifikatorius naudojamas
tipo pavadinimo pakeitimui
• Automatiškai kompiliatorius išplečia iki pilno funkcijos aprašymo
• Gali būti perkrautos kitomis funkcijomis – šablonais
10.14. Klasių šablonai
• Tai klasių apibrėţimai, naudojant specifikaciją template
• Automatiškai juos kompiliatorius išplečia iki pilno klasių apibrėţimo, kaip reikalauja taisyklės
• Negali būti įstatyti į kitas klases (skirtingai nuo įprastų klasių)
• Gali keisti netipizuotus parametrus. Reikšmės, nurodytos šiems parametrams, privalo būti konstantos
• Gali būti išvesti iš kaip iš ne šabloninių klasių, taip ir iš klasių šablonų
• Iš jų gali būti išvestos kaip ne šabloninės klasės, taip ir klasės šablonai
• Kai kuriems tipams gali būti perdengti tam, kad atlikt (arba neatlikti) kokius nors veiksmus, kuriuos klasių šablonai
neatlieka
• Taip pat gali būti klasėmis-struktūromis arba klasėmis-apjungimais
class Stack <char *> {
char * *v;
int size, top;
public:
Stack(int ezis);
~Stack();
void Push(const char * &);
char * & Pop();
char * & Top() const; };
template <class T> const T& Max(const T&, const
T&);
Max(const T&a, const T&b) {
return a>b ? a: b;
}
// ….
int I,j;
float a,b;
// ….
int k=Max(I,j);
// iskvieciama Max(int, int)
float c=Max(a,b); // iskvieciama Max(float, float)
// perkrovimas
template <class T> const T& Max(const T&, const
T&);
template <class T. const T&Max(const T*, int);
int Max(int(*) (int), int (*) (int));
template <class T> class Stack
{
T *v;
int size, top;
pPublic:
Stack(int ezis);
~Stack();
void Push(const TŲ);
T& Pop();
T& Top() const;
};
Stack <int> i; // int duomenims
Stack <char *> cp; // char duomenims
// Netipizuoti duomenys
template <class T, int size> class Stack {
T v[size];
int top;
public:
Stack(): top(-1) { }
}
Stack <int,20> tiny;
Stack <int, 500> huge;
- 56 -
10.15. Statiniai duomenys- nariai
• Yra bendri duomenys visiems tam tikros klasės-šablono egzemplioriaus klasės objektams.
• Apibrėţiami failo matomumo srityje, kuomet prieš apibrėţimą naudojamas template
template <class T> class C {
static int i;
static T t; };
template <class T> int C <T>::i; // apibrėţiame matomumo srityje
template <class T> int C <T>::t;
C <char> c;
C <float> f;
10.16. Funkcijų-narių šablonai
• Apibrėţiami uţ klasių, kurioms priklauso, apibrėţimų ribų naudojant specifikatorių template
• Apibrėţtiems tipams gali būti perdengti, kad atlikti tam tikrus veiksmus, kuriuos metodų šablonai neatlieka
template <class T> void
Stack <T>::Push(const T& element)
{
if(top==size-1)
error(―stack oveflow‖);
else v[++top]=element;
}
10.17. Draugiškos funkcijos
• Kiekvienam T tipui gali būt visų T tipo klasių draugais. Tai įprastos draugiškos klasės
• T tipui gali būt T tipo klasės draugais
• Prieš juos gali eiti specifikatorius template
• Gali būti kitos klasės funkcijomis- nariais
• Gali būti apibrėţti visai klasei
• Gali būti ne šabloninių klasių draugais
10.18. Operacijų perkrovimas
• Gali išplėsti iki klasės tipo bet kurių operacijų reikšmes, įskaitant :: , sizeof, ? :, ., *
• Atliekama deklaruojant funkciją, susidedančią iš raktinio ţodţio operator, po kurios seka viena iš vidinių operacijų
• Nedaro jokių prielaidų apie tokias operacijas. Pvz: jeigu i yra int tipo, tai ++i yra tas pats, kas i=+1 ir i=i+1.
Perkrautiems operatoriams tokios prielaidos nebegalioja tol, kol pats programuotojas jų atitinkamai neapibrėţia
• Negali naudoti argumentų pagal nutylėjimą
10.19. Unariniai operatoriai
• Gali būti apibrėţti kaip nestatinės funkcijos-nariai, neturintys argumentų. Tai yra @ x interpretuojama kaip
x.operator@() bet kuriam operatoriui @
• Gali būti apibrėţti kaip funkcijos, nesančios kokios nors klasės nariais, ir turinčios vieną argumentą
• Šis argumentas yra tos klasės kintamasis arba nuoroda į tokį kintamąjį
• Šiuo atveju @x interpretuojama kaip operator@(x) bet kuriam operatoriui @
- 57 -
11. XI-OJI PASKAITA
Operatorių perkrovimas. Polimorfizmas.
11.1. Unariniai operatoriai
Class X {
X operator-() const; // unarinis minusas
X operator &() const; // adreso gavimas
X operator ^() const; // klaida: ^ binarine operacija
};
class Y {
friend Y operator –(const Y&); // unarinis minusas
friend Y operator &(const Y&); // adreso skaiciavimas
friend Y operator^(const Y&); // klaida
};
11.2. Binarinės operacijos
• Gali būti deklaruotos kaip nestatinės funkcijos, kurios turi vieną argumentą. Tai yra uţrašas x@y interpretuojamas
kaip x.operator@(y) bet kuriam operatoriui @.
• Gali būti deklaruotos kaip funkcijos, nepriklausančios konkrečiai klasei ir turinčios vieną argumentą.
• Šis argumentas turi būti arba duotos klasės kintamuoju, arba nuoroda į tokį kintamąjį. Ta yra bet kuriai operacijai @,
Įskaitant ir operaciją =, x@y interpretuojama kaip operator@(x,y)
• Pvz:
class X {
X operator –(const X&) const; // binarinis minusas
X operator &(const X&) const; // poskiltinis IR
X operator! (const X&) const; // klaida: ! Unarine operacija
};
class Y {
friend Y operator –(const Y&, const Y&);
friend Y operator &(const Y&, const Y&);
friend Y operator! (const Y&, const Y&); // klaida
};
11.3. Operatorių perkrovimas
• class complex {
double re, im;
public:
complex (double r, double i) { re=r; im=i; }
friend complex operator +(complex, complex);
friend complex operator*(complex, complex);
}
• void f() {
complex a= complex(1, 3.1);
complex b = complex(1.2, 2);
complex c = b;
a=b+c;
b=b+c*a;
c=a*b + complex(1,2);
• class matrix {
double m[4][4];
public:
matrix();
friend matrix operator+(matrix&, matrix &);
friend matrix operator*(matrix&, matrix&);
};
• matrix operator + (matrix& arg1, matrix& arg2) {
matrix sum;
for (int i=0; i<4; i++)
for (int j=0; j<4; j++) sum.m[i][j]= arg1.m[i][j] +
arg2.m[i][j];
return sum;
}
- 58 -
11.4. Funkcijos iškvietimo operacija
• Turi būt paskelbta, kaip nestatinė klasės funkcija narys
• Leidţia vartotojui nustatyti operandų kiekį
Class X {
// ....
public:
X(int a, int b, int c);
// ....
void operator() (int i, int j, in k);
};
X Xample(1,2,3); // iškviečiamas konstruktorius
Xamople(4,5,6); // iškviečiama operacija ()
11.5. Priskyrimo operacija
• Naudojama vieno duotos klasės objekto reikšmės priskyrimui kitam objektui
• Jeigu vartotojas jos neapibrėţė, tai atlieka vieno klasės objekto laukų priskyrimą kitam objektui
• Turi būti apibrėţta, jeigu klasėje yra laukai, kurie yra rodikliai į dinamiškai išskirtas atminties sritis
• Tai tik operacijos funkcija, kuri nėra paveldima
• Turi būti apibrėţta, kaip nestatinė klasės funkcija - narys
11.6. Indeksavimo operacija
• Turi būti apibrėţta kaip nestatinė klasės funkcija- narys
• Daţniausiai graţina nuorodą, todėl ją galima naudoti priskyrimo operacijoje iš abiejų operando pusių
class String* {
char *s;
//…
public:
String(char);
String(const char *);
char &operator[](int pos) { return s[pos]; }
// …
};
String ball=mitten‖;
ball[0]=‗k‘;
11.7. Priėjimo prie nario operacija
• X->m interpretuojama kaip (x.operator->())->m.
• Pastebima, kad tai unarinė operacija ir kad x – tai klasės objektas, o ne rodiklis į jį.
• Turi graţinti rodiklį į klasės objektą, arba patį klasės objektą, arba nuorodą į klasės objektą, kuriam ši operacija yra
priskirta, kadangi originali operacijos -> prasmė nėra prarandama, o tik uţlaikoma
• Turi būti apibrėţta, kaip nestatinė klasės funkcija - narys
11.8. Polimorfizmas
• Polimorfizmas išvertus iš graikų kalbos reiškia daugiaformiškumas
• Tai paskutinė bazinė objektinio programavimo koncepcija
• class IntStack {
int *v, size, top;
public:
// …
IntStack& operator=(const IntStack &);
};
IntStack& IntStack:: operator=(const IntStack& from) {
if(this !=&from )
{
delete [] v;
v= new int[size=from.size];
for(register int i=0; i<size; i++)
v[i]=from.v[i];
top=from.top;
}
return *this;
}
// ….
IntStack a(100),b,c;
//….
c=b=a;
- 59 -
• Polimorfizmas objektiniame programavime suprantamas kaip objekto sugebėjimas sureaguoti į tam tikrą uţklausą
priklausomai nuo savo tipo, netgi ir tuo atveju, kuomet kompiliacijos metu objekto, kuriam nukreipta uţklausa, tipas
dar nėra ţinomas.
• C++ kalboje polimorfizmo koncepcija realizuota naudojant virtualių funkcijų-narių mechanzmą
11.9. Virtualios funkcijos-nariai
• Jeigu kurioje nors klasėje yra funkcija, aprašyta kaip virtual, tai į tokią klasę kompiliatorius patalpina paslėptą narį –
rodiklį į virtualių funkcijų lentelę.
• Taip pat sugeneruojamas specialus kodas, leidţiantis parinkti virtualią funkciją, tinkančią duoto tipo objektui,
programos vykdymo metu.
• Šis procesas įgijo vėlyvo surišimo pavadinimą (angl. late binding).
• Kita vertus virtualių funkcijų naudojimas lėtina programos veikimą ir didina klasės objektų dydį
• Jeigu kokia nors funkcija bazinėje klasėje apibrėţta kaip virtuali, tai funkcija tokiu pačiu pavadinimu, su tuo pačiu
argumentų sąrašu ir tokio paties tipo graţinama reikšme, apibrėţta bazinėje klasėje, automatiškai tampa virtualia
• Negalima apibrėţti išvestinėje klasėje funkcijos, besiskiriančios tiktai graţinamos reikšmės tipu
• Virtuali funkcija savaime suprantama turi būti kokios nors klasės metodu, tačiau ji negali būti statiniu kokios nors
klasės metodu.
• Išvestinėje klasėje galima apibrėţti funkciją su kitu besiskiriančiu argumentų sąrašu ir ji bus paprasta (nevirtuali)
funkcija
class Base {
int dummy;
public:
void f() { cout<<f_msg<<―Base::f()\n‖; }
11.10. Programavimas Windows aplinkai
• Palyginus su Dos aplinkai parašytomis programomis, Wiindows programos pasiţymi neįprastu veikimo principu
• Funkcija WinMain gauna darbo pradţioje valdymą ir atlieka tam tikrus veiksmus, inicializuojančius programos langą
• Po to ši funkcija pereina į pranešimų apdorojimo reţimą (ciklą)
• Išėjus iš pranešimų apdorojimo ciklo programos darbas baigiamas
• Funkcijos WndProc neiškviečia nė viena kita programos funkcija, nors būtent ši funkcija atlieka visą naudingą
vartotojo poţiūriu darbą
#define STRICT
#include <windows.h>
//lango klases pavadinimas
char const szClassName[]=―MyWindowsAppClass‖;
//lango pavadinimas
char const szWindowsTitle[]=―MyWindowApplication‖;
Int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow) {
HWND hwnd; // lango identifikatorius
MSG msg;
ATOM aWndClass; // grizimo kodo atomas
WNDCLASS wc; // lango klases registravimo struktura
// uzpildome lango klases registravimo strukturos laukus
//lango stilius
wc.style=0;
// rodikis i lango funkcija, apdorojancia visus sios klases langu pranesimus
wc.lpfnWndPoc=(WNDPROC)WndProc;
wc.cbClsExtra=0;
wc.cbWndExtra=0; // papildomos atminties dydis
wc.hInstance=hInstance;
wc.hIcon=LoadIcon(NULL,IDI_APPLICATION); //ikoneles identifikatorius
wc.hCursor=LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground=(HBRUSH)(COLOR_WINDOW=1);
wc.lpszMenuName=(LPSTR)NULL;
wc.lpszClassName=(LPSTR) szClassName;
// uzregistruojame lango klase
aWndClass=RegisterClass(&wc);
// sukuriam langa
- 60 -
hwnd=CreateWindow(szClassName, szWindowTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
// lango dydis ir vieta
0, // motininio lango identifikatorius
0, // meniu identifikatorius
hInstance, NULL);
if(!hwnd) return FALSE;
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// paleidziam pranesimu apdorojimo cikla
while(GetMessage(&msg,0,0,0)) {
DispatchMessage(&msg); }
return msg.wParam; }
LRESULT WINAPI WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch(msg) {
case WM_LBUTTONDOWN: {
MessageBox(NULL, ―paspaustas kairysis peles klavisas‖, ―pranesimas‖, MB_OK);
return 0; }
case W_RBUTTONDOWN: {
MessageBox(NULL, ―paspaustas desinysis peles klavisas‖, ―pranesimas‖, MB_OK);
return 0; }
case WM_DESTROY: {
PostQuitMessage(0);
return(0);
}
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
11.11. Darbas su Windows sistemos meniu
• Beveik kiekviena Windows aplinkai parašyta programa turi meniu.
• Daţniausiai meniu kuriamas naudojant programos resursų failą ir resursų kompiliatorių
• Resursų failas turi plėtinį .rc
• Resursu faile gali buti ir kai kurie kiti programai reikalingi duomenys (pavyzdţiui akseleratoriai, ikonėlės ir t.t.)
MY_MENU
nameID MENU
BEGIN
POPUP ―&File‖
BEGIN
MENUITEM ―&New‖, CM_NEW
MENUITEM ―&Open‖, CM_OPEN
MENUITEM ―&Save‖, CM_SAVE
END
POPUP ―&Edit‖
BEGIN
MENUITEM ―&Undo‖, CM_UNDO
MENUITEM ―&Cut‖, CM_CUT
MENUITEM ―&Paste‖, CM_PASTE
MENUITEM ―&Copy‖, CM_COPY
END
END
MENUITEM SEPARATOR
Programos fragmentas:
case WM_COMMAND:
{
switch(wParam) {
case CM_NEW:
….
// aprasomi reikalingi veiksmai
return 0;
case CM_UNDO:
…..
// aprasomi Undo veiksma
return 0; // ir t.t. visi meniu