Documentos de Académico
Documentos de Profesional
Documentos de Cultura
C++ PRAKTIKUMAS
Kaunas 2001
Turinys
1 skyrius. C++ elementai..........................................................................5 2 skyrius. Masyvai, failai, eiluts ...........................................................26 3 skyrius. Struktros...............................................................................55 4 skyrius. Klass apibrimas. Objektas ................................................62 5 skyrius. vedimo, ivedimo srautai......................................................67 6 skyrius. Klasi savybs. Objektikai orientuotas programavimas ......77 7 skyrius. Tiesiniai vienkrypiai sraai...............................................103 8 skyrius. Dvikrypiai tiesiniai sraai.................................................136 9 skyrius. Tiesini sra rinkiniai.......................................................147 10 skyrius. Netiesiniai sraai ..............................................................152 11 skyrius. Savarankikas darbas .........................................................158
vadas
C kalba ir jos modifikacija C++ pasiymi labai dideliu lakonikumu, sintaksini struktr lankstumu ir universalumu, todl i kalb daniausiai pradedama mokytis jau turint programavimo kitomis kalbomis patyrim. i knyga skirta skaitytojui, kuriam inomos pagrindins programavimo kalbose vartojamos svokos, program struktrizavimo priemons, kuris sugeba naudoti standartines duomen struktras, valdanias struktras, programavimo aplinkas. Knygelje glaustai pateikiamos C++ pagrindins preimons (sakini konstrukcijos, duomen tipai, funkcijos). Gausiai pavyzdiais iliustruojamos pagrindins C++ priemons, kurios, autori nuomone, btinos pradinei painiai su ia kalba. Pagrindinis dmesys skirtas klasms ir j savybms. Programavime plaiai vartojamas dinaminis atminties skirstymas. Tai leidia kurti efektyvesnes programas. Dinamins duomen struktros leidia kurti programuotojui norimos konfigracijos ir sudtingumo duomen tipus. Tai labai lanksti ir efektyvi priemon, naudojama duomenims saugoti bei apdoroti. iame leidinyje pristatomos pagrindins klasikins dinamins duomen struktros: tiesiniai vienkrypiai sraai ir j modifikacijos (stekas, dekas, eil, iedas), dvikrypiai tiesiniai sraai, tiesini sra rinkiniai, medio tipo sraas. Nagrinjamos pagrindins operacijos su sraais: formavimas, perira, paieka, rikiavimas, alinimas, terpimas, naikinimas. Visi veiksmai iliustruojami realiomis programomis, paraytomis ir patikrintomis su C++4.5. Knyga skirta studentams, studijuojantiems duomen struktras, taiau stengtasi pristatyti dinamines duomen struktras detaliai su kuo daugiau pavyzdi, kad bt suprantama ir ingeidiam moksleiviui ar programavimo mgjui. i knyga skirta praktinei painiai su C++, todl isamesnms kalbos studijoms reikalinga specialiai tam skirta literatra. C++ kalbai skirt knyg daug, taiau jos silomos usienio kalbomis. Lietuvikai parayt knyg labai maai. Autoriai tikisi, kad i knyga palengvins pradedaniam programuotojui pasirinkti jam tinkam literatr.
Apraant programos struktr, panaudotos tokios C++ kalbos sintaksins konstrukcijos: komentarai paaikinimams skirtas simboli rinkinys, kurio ribos ymimos simboli poromis /* ir */; paraius eilutje du simbolius be 5
tarpo // toliau esantis tekstas iki eiluts pabaigos suprantamas kaip komentarai; main() pagrindins programos funkcijos antrat; sudtinis sakinys tarp skliaust {} raytas sakini rinkinys, su kuriais programoje elgiamasi taip kaip su vienu sakiniu;
Programos struktros aprayme komentarais parodyta, kokia tvarka joje turi bti surayti struktriniai elementai: Programos pradioje suraomos instrukcijos pirminiam procesoriui (preprocessor), kuriose nurodoma, kokius programos teksto pertvarkymus reikia atlikti prie jos kompiliavim. Globaliniais apraais apibriami tie programos objektai, kurie gali bti vartojami visame rengiamos programos faile (tiek pagrindinje, tiek vartotojo funkcijose). Vartotojo funkcijos gali bti apraomos ne tik programos gale, bet ir globalini apra dalyje. Suraius pagalbines funkcijas programos gale, jos tekst lengviau skaityti ir analizuoti; tada nesilaikoma reikalavimo, kad funkcijas reikia pirma aprayti, o tik po to vartoti. io prietaravimo ivengiama globalini apra dalyje pateikiant programos gale surayt funkcij prototipus. Funkcij prototipais vadinami j antrai sakiniai. Programai tikslinga suteikti vard, uraom teksto pirmoje eilutje kaip komentar. ia naudinga parayti daugiau paaikinim apie program. Visa tai leis lengviau ir greiiau atpainti reikaling program.
void Atsakymas(int); // // Pagrindin programa void main() { // char simbolis; // int Dangus; // cout << "Ar saule sviecia?\n"; //
} // void Atsakymas(int Ats) if (Ats == 1) cout << else cout << "danguje }
cout << " Taip 1 Ne 0 \n"; cin >> Dangus; // vedimas klaviatra cout << "Atsakymas:" << Dangus << "\n"; cout << "Dabar "; Atsakymas(Dangus); // Kreipinys funkcij cout << endl << "Paspauskite bet kok simbolini klavi << " ir ENTER" << endl; // endl eiluts pabaiga cin >> simbolis; Funkcija { "giedras dangus!!!\n"; labai daug debesu\n";
Programos pradioje suraytos instrukcijos pirminiam procesoriui, kurios ymimos simboliu #. terpimo instrukcijomis include nurodoma, koki fail tekstai turi bti terpti instrukcij paymtose vietose pirminio apdorojimo metu. terpiam fail vardai raomi tarp simboli < > arba tarp kabui (""), jeigu failas yra sukurtas vartotojo. Jeigu programoje vartojamos asmenins pagalbins bibliotekos, instrukcijomis include turi bti nurodomi iose bibliotekose esani priemoni prototip failai, kuri vardai turi pltinius .h (header).
Kaip ir kitose kalbose, duomen tipai nurodomi baziniais odiais. C++ kalboje yra vartojami ie baziniai elementars duomen tipai:
1.1 lentel. Pagrindiniai tipai
Tipas
char int long short unsigned float double
Galimos reikms
128..127 32768..32768 2147483648..2147483647 32768..32768 0..65535 3.41038..3.41038 1.710308..1.710308
Programoje visi kintamieji kiekvienu momentu privalo bti apibrti, t.y. turti konkrei reikm. Kalbos kompiliatorius kintamiesiems skiria atmint, taiau nepasirpina, kas ten yra. Programuotojas privalo tai numatyti. Rekomenduojama programos kintamj apra dalyje nurodyti pradines j reikmes. Programos pavyzdyje galima buvo parayti: int Dangus = 0; arba atskiru priskyrimo sakiniu: Dangus = 0; Aprauose vienodo tipo kintamuosius galima grupuoti, pavyzdiui:
int Dangus = 0, Medis = 0, Katinas = 0;
Nors abu bdai duoda t pat rezultat, taiau pirmasis tinkamesnis. Jis vaizdesnis ir suprantamesnis, nes vienoje vietoje viskas pasakyta apie kintamj. J naudojant bus maiau klystama, nes nereiks dukart kartoti kintamojo vardo. Jis leidia lengviau suprasti ir modifikuoti program, nes kintamj reikms pasakomos j aprao vietoje ir kiekviena inicializuojama atskirai, taigi, ir pakeitimus darant, pradines reikmes galima skirti skirtingas. Sveikojo tipo kintamj aprayme galima taikyti nuorod unsigned. Taip sukuriami kintamieji, galintys turti tik teigiamas sveiko tipo reikmes, pavyzdiui: unsigned int X; // reikmi intervalas: 0..65535 Programose labai naudingos konstantos. Jos apraomos panaudojant kalbos rezervuot od const, pavyzdiui: const const 8 int float A = 125; B = 13.5;
1.2 pratimas. Programa parodo, kad C++ kalboje leidiama maiyti skirting tip kintamuosius. Maiant tipus char ir int, naudojamas simbolio ASCII lentels skaitmenis atitikmuo, o maiant tipus int ir float, paprasiausiai pridedama arba atmetama trupmenin skaiiaus dalis.
void main() { int a, b, c; char x, y; float num, toy; a = b = c = 27; x = y = 'A'; num = toy = 3.6792; a = x = num c = } y; b; = b; toy; // // // // a bus lygus 65 (simbolis A) x bus lygus 27 (simbolis ) num bus lygus 27.00 c bus lygus 3
Kintamasis galioja nuo jo paskelbimo vietos. Kintamuosius galima aprayti bet kurioje vietoje. Tikslinga prisiminti, kad: programos teksto pradioje prie main funkcij surayti kintamieji yra globals ir galioja visame tolesniame tekste; kintamieji, kuri apraas yra funkcijoje, vadinami lokaliais ir galioja tik joje; esant vienodam globalaus ir lokalaus kintamojo pavadinimams, pirmenyb suteikiama lokaliam, t.y. toje funkcijoje globalus negalioja; kintamieji gali bti apraomi j panaudojimo vietoje, taiau tai nerekomenduojama.
1.3 pratimas. i programa iliustruoja, kaip galima aprayti kintamuosius, ir kuriose programos vietose jie galios.
#include <stdio.h> void head1(void); void head2(void); void head3(void); int count; // Globalus kintamasis
for (index = 8; index > 0; index) { int stuff; // stuff galioja tik i skliausteli ribose for (stuff = 0; stuff < 7; stuff++) printf("%d ", stuff); printf (" indeksas = %d\n", index); } } int counter; // counter galioja tik nuo ios vietos // Pirmoji antrat void head1(void) { int index; // index galioja tik funkcijoje head1 index = 23; printf("Pirmoji antraste: %d\n", index); } // Antroji antrat void head2(void) { int count; // count galioja tik funkcijoje head2 // ir panaikina globalaus kintamojo galiojim count = 53; printf("Antroji antraste: %d\n", count); counter = 77; } // Treioji antrat void head3(void) { printf("Trecioji antraste: %d\n", counter); }
= = = =
8 7 6 5
10
0 0 0 0
1 1 1 1
2 2 2 2
3 3 3 3
4 4 4 4
5 5 5 5
6 6 6 6
= = = =
4 3 2 1
Priskyrimo sakinyje panaudojome operatori = (lygybs enklas). Kiti daniausiai vartojami operatoriai parodyti 1.2 lentelje. Operacijoms ymti naudojami enklai surayti 1.3 lentelje.
1.2 lentel. Daniausiai naudojami operatoriai
Operatorius
++ += = k++ ++k k k s += k; s = k;
Pavyzdiai ir paaikinimai k reikm panaudojama, po to didinama vienetu. k reikm didinama vienetu, po to panaudojama. k reikm panaudojama, po to mainama vienetu. k reikm mainama vienetu, po to panaudojama.
s = s + k; s = s k;
1.3 lentel. Operacij enklai
Aritmetins operacijos
+ % sudtis atimtis dalybos modulis * / daugyba dalyba
== != >
&& !
Sulyginimo operacijos ar lygios dvi reikms? ar pirma maesn u antr? < ar nelygios dvi reikms? >= ar pirma nemaesn antr? ar pirma didesn u antr? <= ar pirma nedidesn u antr? Logins operacijos
login daugyba ir (and) loginis neigimas ne (not) login sudtis arba (or)
Raant aritmetines iraikas naudojami paprasti skliaustai. Standartins matematins funkcijos yra suraytos bibliotekoje math.h. 1.4 pratimas. Programoje naudojamos vairios palyginimo operacijos.
void main() { int x, y, z; char a, b, c; float r, s, t;
11
x = y = z = 11; a = b = c = 40; r = s = t = 12.987; if if if if if (x == y) z = -13; (x > y) a = 'A'; (!(x > y)) a = 'B'; (b <= c) r = 0.0; (r != s) t = c/2; printf("1. printf("2. printf("3. printf("4. printf("5. z a a r t = = = = = %d\n", %d\n", %d\n", %f\n", %f\n", z); a); a); r); t);
if (x = (r != s)) z = printf("6. x = %d z = if (x = y) z = 222; printf("7. x = %d z = if (x != 0) z = 333; printf("8. z = %d\n", if (x) z = 444; printf("9. z = %d\n",
x = y = z = 77; if ((x == y) && (x == 77)) z = 33; printf("10. z = %d\n", z); if ((x > y) || (z > 12)) z = 22; printf("11. z = %d\n", z); if (x && y && z) z = 11; printf("12. z = %d\n", z); if ((x = 1) && (y = 2) && (z = 3)) r = 12.00; printf("13. x = %d y = %d z = %d r = %f\n", x, y, z, r); if ((x == 2) && (y = 3) && (z = 4)) r = 14.56; printf("14. x = %d y = %d z = %d r = %f\n", x, y, z, r); if (x == x) printf("15. if (x != x) printf("16. } z z z z = = = = 1.234; %d\n", z); 5.678; %d\n", z); // visada vykdoma slyga // niekada nevykdoma slyga
Programos rezultatai:
1. 2. 3. 4. 5. 6. z a a r t x = = = = = = -13 40 66 0.000000 20.000000 1 z = 1000
12
7. x = 11 z = 222 8. z = 333 9. z = 444 10. z = 33 11. z = 22 12. z = 11 13. x = 1 y = 2 z = 3 r = 12.000000 14. x = 1 y = 2 z = 3 r = 12.000000 15. z = 1 16. z = 1
1.3. Funkcijos
Programos Pratimas1 pabaigoje parayta funkcija Atsakymas, kurios prototipas uraytas prie pagrindin funkcij main. Funkcijos prototipo uraas baigiamas simboliu ; (kabliatakis). Funkcijos aprao sintaks:
[reikms tipas] vardas ([formali parametr sraas]) { funkcijos tekstas }
Jeigu funkcijos vardui yra suteikiama reikm, jos tekste privalo bti sakinys
return reikm;
Jeigu tokio sakinio nra, funkcijai reikm nesuteikiama. Funkcijos reikms tipas yra nurodomas odiu void (tuias), jeigu funkcija negrina reikms. Reikmi neturinios C++ kalbos funkcijos atitinka kitose kalbose (pavyzdiui Paskalio) procedras. Kreipiniai tokias funkcijas programose sudaro atskirus sakinius. Jeigu funkcija neturi argument, argument sraas gali bti paliekamas tuias arba ymimas odiu void. Pavyzdiui, void Atsakymas(void); Funkcijos prototipe nra tikslo vardinti parametrus: kompiliatoriui pakanka inoti, kiek yra parametr ir kokie j tipai, todl aprae vardai gali bti praleidiami. Pagrindin funkcija main(), kuri programoje gali bti tik viena, nurodo kompiliatoriui, kur prasideda programoje vykdom veiksm apraymai. 13
Pagrindins funkcijos ir pagalbini funkcij tekstai yra sudaromi pagal tas paias taisykles. Tekst sudaro funkcijoje vartojam objekt apraai, programos vykdom skaiiavim ir valdymo sakiniai, komentarai. Funkcija skaiiavim rezultatus gali perduoti jos vartotojui dviem bdais: per parametr sra; per funkcijos vard. Norint gauti funkcijos skaiiavim rezultatus per parametr sra, reikia perduoti kreipinio metu adres vietos, kur turi bti patalpinti grinami duomenys. Adresus gali saugoti rodykls tipo parametrai (r. 26 psl.). Funkcijos prototipe nurodomi rodykls tipo parametrai, pavyzdiui: void Keisti(int *, int); ia pirmasis parametras rodo, kad kreipinio metu reikia perduoti funkcijai int tipo kintamojo adres, kai tuo tarpu antrasis parametras reikalauja reikms, kuri gali bti nurodoma kreipinio metu kaip konstanta, kintamasis ar rodykl reikm. Pavyzdiui: Keisti(&a, 5); Keisti(&a, x); Keisti(&a,*p); formas. 1.5 pratimas. Funkcija Keisti demonstruoja abi parametr Pagrindin funkcija parodo ekrane gaunamus rezultatus.
#include <iostream.h> #include <conio.h> void Keisti(int *, int); // Pagrindin programa void main() { int a = 20, b = 10, *c = &a; cout << " a b cout << "Pradines reiksmes : " << a << " " << b << " " << *c Keisti(&a, b); cout << "Po pirmo keitimo : " << a << " " << b << " " << *c Keisti(&b, 20); cout << "Po antro keitimo : " << a << " " << b << " " << *c Keisti(c, b); cout << "Po trecio keitimo : " << a << " " << b << " " << *c Keisti(&b, *c);
14
} // Funkcija Keisti void Keisti(int *p1, int p2) { *p1 = *p1 + 10; p2 = p2 + 10;
cout << "Po ketvirto keitimo: " << a << " " << b << " " << *c << "\n";
Vardas cout ymi standartin ivedimo sraut (ekran). C++ kalbos simboli eiluts yra tarp dvigub kabui (") rayti kompiuterio alfabeto ir valdani simboli rinkiniai. Valdantys simboliai gali bti terpiami bet kurioje eiluts vietoje (1.4 lentel). J sintaks: \simbolis
1.4 lentel. Valdantys simboliai
Pavadinimas Skambutis Eiluts pabaiga kit eilut kitos eiluts pradi Horizontali tabuliacija Vertikali tabuliacija Nulinis simbolis
C kalboje
\a \r \f \n \t \v \0
ASCII kodas 7 13 12 10 9 11 0
Daniausiai vartojamas valdantis simbolis \n, kuris perkelia ekrano ymekl naujos eiluts pradi. ymeklio perklimui naujos eiluts 15
pradi rekomenduojama naudoti operatori endl. Jeigu norime, kad simboli eilutje bt terptas simbolis " arba \, vartojami atitinkamai \" arba \\. Rezultat formatavimui bibliotekoje iomanip.h saugomi manipuliatoriai, kurie gali bti terpiami ivedimo srautus:
setw (n) setprecision (n)
Jei setw nurodyto lauko dydio skaiiui nepakanka, manipuliatorius ignoruojamas. Nuoroda setw galioja artimiausiai ivedamai reikmei, o setprecission iki naujo nurodymo. 1.6 pratimas. Programa demonstruoja, kaip veikia manipuliatoriai setw ir setprecission.
#include <iostream.h> #include <iomanip.h> main() { float A = 18.236; cout << "1. A=" << setw(9) << A << endl; cout << "2. A=" << setprecision(3) << A << endl; cout << "3. A=" << setw(10) << setprecision(5) << A <<endl; A = 123.45678; cout << "4. A=" << A << endl; }
Skaitymas i standartinio vedimo srauto (klaviatros): Klaviatroje renkami duomenys sudaro ASCII kodo simboli sraut, kurio interpretavimo bd nurodo kintamojo, kuriam nukreipiami duomenys, tipas.
16
Be i sraut klasi, C++ kalboje yra vartojamos ir klasikins vedimo/ivedimo bibliotekos. Tokios bibliotekos yra trys: stdio.h (klasikinis buferizuotas I/O input/output), io.h (emo lygio nebuferizuotas UNIX standarto I/O), conio.h (specialios I/O funkcijos). Pratimuose analizuojamos tik bibliotekos stdio.h funkcijos. Apraant I/O operacijas, vartojama srauto svoka. Srautas tai abstrakcija, leidianti atsiriboti nuo konkretaus I/O renginio (disko, juostos, terminalo). Srautai bna dviej tip: Tekstinis srautas eilutes, atskiriamas specialiais simboliais (CR carriage return, LF line feed), suskaidyta simboli seka. Tekstinio srauto informacija nebtinai yra identika duomenims matomiems konkreiame atvaizdavimo renginyje (spausdintuve, displjuje), nes dalis simboli (CR, LF) yra skirta ne vaizdavimui, o srauto perdavimo valdymui; Dvejetainis srautas bit seka, tiksliai atitinkanti informacij konkreiame renginyje.
Kiekviena programa automatikai atidaro 5 standartinius tekstinius srautus: stdin (vedimas), stdout (ivedimas), stderr (praneimai apie klaidas), stdaux (papildomas), stdprn (spausdintuvas). Jie susiejami su standartiniais sisteminiais I/O renginiais. Daniausiai srautai stdin, stdout, stderr yra susiejami su konsole (klaviatra ir ekranas). Standartin I/O rengin galima pakeisti (perskirti) DOS priemonmis. Standartini sraut (klaviatros ir ekrano) valdymui vartojamos funkcijos printf ir scanf. J prototipai:
int printf (const char *ablonas, [<Kintamj sraas>]); int scanf(const char *ablonas, <Atminties lauk sraas>);
17
ablonas tai simboli eilut su terptais raom arba skaitom duomen formatais, kurie aprao duomenims skiriamo lauko struktr arba j interpretavimo bd. Funkcijoje scanf skaitomiems duomenims skiriami atminties laukai yra nurodomi adresais. Tipin formato struktra yra tokia: %[<Poymis>][<Lauko dydis>] [.<Tikslumas>]<Duomen tipas> Poymiai daniausiai naudojami tokie: sulygiuoti pagal kairj krat; + ivedant skaii, nurodyti jo enkl (+ ar ); Duomen tipai ymimi raidmis. Pagrindiniai tipai yra tokie: d arba i sveikasis, o atuntainis sveikasis, u sveikasis, be enklo, x eioliktainis sveikasis, c simbolinis tipas, f slankaus kablelio, s simboli eilut, e rodiklin forma, p rodykl. 1.7 pratimas. Programa iliustruoja komandos printf veikim.
void main() { int a; long int b; short int c; unsigned int d; char e; float f; double g; a b c d e f g = = = = = = = 1023; 2222; 123; 1234; 'X'; 3.14159; 3.1415926535898; a a a b = = = = %d\n", a); %o\n", a); %x\n", a); %ld\n", b); // // // // deimtainis skaiius atuntainis skaiius eioliktainis skaiius deimtainis ilgas skaiius
18
printf("5. c = %d\n", c); printf("6. d = %u\n", d); printf("7. e = %c\n", e); printf("8. f = %f\n", f); printf("9. g = %f\n", g); printf("\n"); printf("10. a = %d\n", a); printf("11. a = %7d\n", a); printf("12. a = %7d\n", a); c = 5; d = 8; printf("13. a = %*d\n", c, a); printf("14. a = %*d\n", d, a); printf("\n"); printf("15. f = %f\n", f); printf("16. f = %12f\n", f); printf("17. f = %12.3f\n", f); printf("18. f = %12.5f\n", f); printf("19. f = %12.5f\n", f); }
// // // // //
deimtainis trumpas be enklo simbolis realus skaiius dvigubo tikslumo realus sk.
// paprastas sveikas skaiius // lauko plotis 7 simboliai // lygiuojama pagal kair krat // lauko plotis 5 simboliai // lauko plotis 8 simboliai // // // // // paprastas realus skaiius lauko plotis 12 simboli realiai daliai 3 simboliai realiai daliai 5 simboliai lygiuojama pagal kair krat
10. 11. 12. 13. 14. 15. 16. 17. 18. 19.
a a a a a f f f f f
= 1023 = 1023 = 1023 = 1023 = 1023 = 3.141590 = 3.141590 = 3.142 = 3.14159 = 3.14159
19
Kartojamas sakinys yra vykdomas tol, kol kartojimo slyga yra tenkinama (true). Jei ciklo viduje kintamasis, nuo kurio priklauso kartojimo slygos nutraukimas, nra keiiamas, ciklas taps aminu. Ciklas nei karto nebus vykdomas, jeigu kartojimo slyga jau pradioje bus netenkinama (false). Jeigu cikl sudaro keletas sakini, tuomet jie skliaudiami skliaustais {}. Taip gaunamas sudtinis sakinys. 1.8 pratimas. Programa iliustruoja ciklo while veikim.
void main() { int count = 0; while(count < 6) { printf("count = %d\n", count); count++; } }
Nuo ciklo while is ciklas skiriasi tuo, kad jis visuomet bus vykdomas bent vien kart, nes kartojimo slyga tikrinama jau vykdius kartojam sakin. 1.9 pratimas. Programa iliustruoja ciklo dowhile veikim.
void main() { int i = 0; do { printf("i = %d\n", i); i++; } while(i < 5);
20
Sakinyje nurodyta iraika i1 skaiiuojama tik vien kart, prie pradedant cikl, kuomet tikrinama, ar tenkinama i2 iraika. Jei taip vykdomas kartojimo sakinys. Iraikos i3 reikm perskaiiuojama po kartojimo sakinio vykdymo, ir grtama prie iraikos i2 tikrinimo. Taigi, galime apibendrinti, kad i1 apibria ciklo parametro pradin reikm, i2 nurodo sustojimo slyg, o i3 kitimo dsn. 1.10 pratimas. Programa iliustruoja ciklo for veikim.
void main() { int i; for(i=0; i<6; i++) printf("i = %d\n", i); }
Valdymo struktros. Pradsime nuo vienos i populiariausi struktros ifelse. Jos sintaks yra tokia:
if (slyga) <aka TAIP>; else <aka NE>;
C++ kalboje nra loginio duomen tipo, todl santykiams suteikiama skaitmenin reikm, kuri yra 1, kai santykio operacija tenkinama, ir 0, 21
kai santykio operacija netenkinama. C++ kalboje slygas galima aprayti ne tik santykiais, bet ir bet kokiomis kitomis skaitmeninmis iraikomis. Jei tokios iraikos reikm yra 0, laikoma, kad slyga netenkinama, o jei reikm kitokia, laikoma, kad slyga tenkinama. Dar viena domi C++ kalbos savyb kalboje nra loginio tipo, bet logines iraikas vartoti galima. Logini iraik skaitmeniniai argumentai, kuri reikms nenulins (0), interpretuojami kaip logins reikms TRUE, o nulins (=0) laikomos reikmmis FALSE. Logini iraik reikms taip pat bna skaitmenins 0 (FALSE) ir 1 (TRUE). Programuojantiems Paskalio kalba, siloma atkreipti dmes tai, kad: C++ kalbos sakinyje if prie ak else yra raomas ; (kabliatakis). vertinant tai, kad logins reikms C++ kalboje nra, teisingas bus uraas:
if (Ats) cout << "giedras dangus!!!\n"; else cout << "danguje labai daug debesu\n";
ia else aka bus vykdoma tik esant atsakymo nulinei reikmei, kitais atvejais bus vykdoma aka TRUE. Jeigu programos vartotojas netiksliai atsakys programos klausim, tuomet is sakinys ir programos pavyzdyje esantis sakinys dirbs skirtingai. Aukiau pateiktoje programoje 1.1 pratimas else aka bus vykdoma visais atvejais, iskyrus atsakym lyg 1. 1.11 pratimas. Programa iliustruoja valdymo struktros ifelse veikim.
void main() { int data; for (data=0; data<10; data++) { if (data == 2) printf("data = %d\n", data); if (data < 5) printf("data = %d, t.y. maziau uz 5\n", data); else printf("data = %d, t.y. daugiau uz 4\n", data); } }
22
= = = = = = = =
2, 3, 4, 5, 6, 7, 8, 9,
Dar du nauji valdymo sakiniai, tai break ir continue. ie sakiniai neturi joki parametr ir daniausiai yra naudojami cikluose; break nutraukia ciklo vykdym ir veiksmai tsiami nuo pirmo sakinio, esanio u ciklo, o continue nutraukia tik vienos iteracijos vykdym ir sugrina kartojimo slygos tikrinim. 1.12 pratimas. Programa iliustruoja sakini break ir continue veikim.
void main() { int xx; for (xx=5; xx<15; xx++) { if (xx == 8) break; printf("Break ciklas; xx = %d\n", xx); } for (xx=5; xx<15; xx++) { if (xx == 8) continue; printf("Continue ciklas; xx = %d\n", xx); } }
5 6 7 9 10 11 12 13
23
Continue ciklas; xx = 14
Jei iraika yra lygi ym1, bus vykdomi sakinys1, sakinys2 ir sakinys3; jei lygi ym2 sakinys2 ir sakinys3, o visais likusiais atvejais tik sakinys3. Taiau daniau i struktra yra naudojama kartu su operatoriumi break:
switch (iraika) { case ym1: sakinys1; break; case ym2: sakinys2; break; default : sakinys3; break;
iuo atveju, jei iraika lygi ym1, vykdomas tik sakinys1; jei lygi ym2 tik sakinys2, o likusiais atvejais sakinys3. 1.13 pratimas. Programa iliustruoja struktros switch-case veikim.
void main() { int i; for(i=3; i<13; i++) { switch (i) { case 3 : printf("Trys\n"); break; case 4 : printf("Keturi\n"); break; case 5 : case 6 : case 7 : case 8 : printf("Penki - astuoni\n"); break; case 11 : printf("Vienuolika\n"); break; default : printf("Neaprasyta reiksme\n"); break; } } }
24
25
Programos tekste struktra *rodykls vardas reikia, kad turi bti vartojama rodykls rodomo lauko reikm. Paioms rodyklms gali bti suteikiamos tik to paties tipo objekt, kuriems buvo apibrtos rodykls, adres reikms. Pavyzdys: int x, *px;
px = &x;
Priskyrimo operatorius px = &x rodyklei px suteikia kintamojo x adreso reikm, todl x reikm dabar galima kreiptis tiek vardu x, tiek struktra *px. Rodykls tipo kintamiesiems btina nurodyti pradin reikm, kuri daniausiai bna tuias adresas: px = NULL. 2.1 pratimas. Programa iliustruoja, kaip naudojamos rodykls. Kintamieji pt1 ir pt2 yra rodykls (tai rodo vaigduts prie juos). Ura &index galime skaityti kaip kintamojo index adresas. O realiai ioje programoje yra tik vienas kintamasis, kurio reikm keiiame. 26
main() { int index, *pt1, *pt2; index = 39; pt1 = &index; pt2 = pt1; printf("index *pt1 *pt2"); printf("%4d %4d %4d\n", index, *pt1, *pt2); *pt1 = 13; printf("%4d %4d %4d\n", index, *pt1, *pt2); }
iame aprayme skliaustai [ ] yra btini struktros elementai. Masyvo narius ymi kintamieji su indeksais, kuri vard sintaks:
masyvo vardas[indeksas]
Pavyzdiui, masyvo apraas: int A[10]; masyvo elementas: A[5]. Indeksais gali bti tik intervalo [0, n-1] sveikosios reikms (n masyvo element skaiius). Pastaba: Masyvo vardas be indekso reikia jo nulinio elemento adres. 2.2 pratimas. Programa demonstruoja situacij, kai gretimuose atminties laukuose surayti vienodo tipo duomenys (a, b, c, d) panaudoti masyvo formavimui. Pradi nurodo pirmojo kintamojo adresas: pi = &a;
#include <iostream.h> int a=1, b=2, c=3, d=4, *pi;
27
main() { int i = 0, mas[4]; pi = &a; // Kintamojo a adresas while(i<4) mas[i++] = *(pi++); // Masyvo formavimas // Masyvo ivedimas ekran atvirkia tvarka while(i) cout << mas[--i] << "\t"; cout << endl; }
Kai reikmi didinimo ar mainimo operacijos yra taikomos rodyklms, j reikms yra keiiamos ne vienetu, o su rodykle susieto lauko dydiu. i rodykli savyb pavyzdio programoje yra panaudota kintamj grups a, b, c, d reikmi perrinkimui. Papildykite program taip, kad ji parodyt ekrane vis joje vartojam kintamj ir masyvo mas element adresus. Apraant adres ivedim, naudokite rodykli ablon %p. Adresai bus ivedami eioliktainje skaiiavimo sistemoje. 2.3 pratimas. Reikia surasti masyvo element sum. Duomenys vedami klaviatra. Reikmms sumuoti padaroma funkcija, kuri rezultat grina funkcijos vardu. Funkcijai perduodamas masyvo pirmojo elemento adresas: A (galima rayti &A[0]).
#include <iostream.h> int Suma(int *, int); // Pagrindin programa void main() { int A[10], i, n; // A[0], A[1], ... , A[9] cout << "Iveskite n reiksme: "; cin >> n; for (i=0; i<n; i++) { cout << "A[ " << i << " ]= "; cin >> A[i]; } cout << " Suma = " << Suma(A, n) << endl; } // Funkcija Suma int Suma(int *pr, int k) { int S = 0, i = 0; while(i<k) { S += *(pr+i); i++; } return S; }
28
ia nurodomas praomos atminties Kiekis baitais. Jeigu atmintis buvo skirta, tai grinama rodykl pirmj tos atminties bait, kitaip funkcijos reikm yra NULL. Kreipinio metu atminties kiek patogu nurodyti kaip duomen element skaiiaus ir vieno elemento uimamos vietos baitais sandaug, pavyzdiui: n * sizeof(float) kur n reali skaii kiekis, o funkcija sizeof grina nurodyto duomen tipo (ia realaus) apimt baitais. Funkcija malloc grina rodykl nurodyto tipo atmint: kreipinio metu nurodomas rodykls tipas. 2.4 pratimas. Klaviatra vedamas duomen skaiius. Jeigu duomenims saugoti yra vietos atmintyje, tai duomenys vedami klaviatra masyv, kurio rodykl yra saugoma kintamuojame A. Toliau duomenys surikiuojami majimo tvarka.
#include <iostream.h> #include <alloc.h> // Atminties skyrimas ir duomen vedimas int *Duomenys(int); void Tvarka (int *, int); // Rikiavimas void Sp (int *, int); // Ivedimas ekrane // Pagrindin programa void main() { int n, *A; cout << "Iveskite n reiksme: "; cin >> n; A = Duomenys(n); if (A != NULL) { cout << " n= " << n << endl; cout << " Ivestas masyvas:\n"; Sp(A, n); Tvarka(A, n); cout << " Sutvarkytas masyvas:\n";
29
Sp(A, n); free(A); } // Masyvo uimama atmintis atlaisvinama cout << endl; } // Duomen vedimas int *Duomenys(int n) { int *x, i; x = (int *) malloc(n * sizeof(int)); if (x == NULL) cout << "Truksta atminties" << endl; else { for(i=0; i<n; i++) { cout << "Iveskite " << (i+1) << "-a elementa "; cin >> *(x+i); } cout << endl; } return x; } // Rikiavimas void Tvarka(int *x, int n) { int i, j, c; for(i=0; i<n-1; i++) for(j=i+1; j<n; j++) if (*(x+j) > *(x+i) ) { c = *(x+i); *(x+i) = *(x+j); *(x+j) = c; } } // Ivedimas ekrane void Sp(int *x, int n) { int i = 0; while(i < n) { cout << (i+1) << "-elementas: " << *(x+i) << endl; i++; } }
Atminties iskyrimui patogu naudoti operatori new. Pakeiskite 2.3 pratimo funkcijos Duomenys eilut:
x = (int *) malloc(n * sizeof(int));
nauja eilute:
x = new int[n];
Isiaikinkite
new
galimybes,
taikym
Pavyzdiui, uraas int A[10][8] rodo, kad bus 10 eilui (0, 1, 2, , 9) ir kiekviena j turs po 8 elementus (stulpeliai 0, 1, 2, , 7). Matricos elementas veiksmuose nurodomas dviem indeksais, kurie raomi atskiruose lautiniuose skliaustuose: A[2][3]. Sudtingesniuose udaviniuose tikslinga sukurti savo duomen tipus. Masyvo tipas sukuriamas taip: typedef vardas Pavyzdiui:
typedef int Masyvas [55]; typedef float Matrica [10][15]; const Kiek = 15 typedef double Lentele [Kiek][Kiek];
masyvo
element
tipas
masyvo
tipo
indeks apraas;
ia buvo sukurti fiksuotos apimties masyv tipai. Dabar galima aprayti kelet to paties tipo masyv: Masyvas A, B, C;
Matrica P, D, R; Lentele Pirma, Antra;
2.5 pratimas. Klaviatra vedami duomenys, kurie saugomi matricoje. Suskaiiuojama matricos kiekvienos eiluts element suma. Rezultatai suraomi vienmat masyv.
#include <iostream.h> #include <alloc.h> typedef int mas [10]; typedef int matr[10][15]; int Duomenys(matr, int *, int *); void Sp (mas, int); int Suma (mas, int); // Duomen vedimas // Spausdinimas // Masyvo element suma
31
// Pagrindin programa void main() { matr A; mas S; int n, m, i, j; Duomenys(A, &n, &m); // Duomen vedimas klaviatra if (A != NULL) { cout << "----------------------\n"; for (i=0; i<n; i++) // Matricos spausdinimas for (j=0; j<m; j++) cout << A[i][j] << endl; cout << "----------------------\n"; for (i=0; i<n; i++) { // Matricos spausdinimas cout << "Eilute Nr " << (i+1) <<": "; Sp(A[i], m); } // Vienos eiluts spausdinimas cout << "----------------------\n"; // Skaiiavimai for (i=0; i<n; i++) S[i] = Suma(A[i], m); Sp (S, n); } // Suformuoto masyvo spausdinimas cout << endl;
} // Duomen vedimas int Duomenys(matr D, int *n, int *m) { int i, j; cout << "Iveskite n reiksme: "; cin >> *n; cout << "Iveskite m reiksme: "; cin >> *m; for (i=0; i<*n; i++) { cout << "Eilute Nr:" << (i+1) << endl; for (j=0; j<*m; j++) { cout << "Iveskite " << (j+1) << "-a elementa "; cin >> D[i][j]; } } cout << endl; return 1; } } // Spausdinimas void Sp(mas x, int n) { int i = 0; while (i<n) cout << x[i++] << " " << endl; } // Masyvo element sumos skaiiavimas int Suma(mas D, int k) { int i, Sk=0; for (i=0; i<k; i++) Sk += D[i]; return Sk; }
32
kuri
suformuot
rezultat
masyv.
i funkcija, vis matricos eilui element sumas surao vienmat masyv, kurio vardas nurodomas kreipinio metu pirmuoju parametru.
void Suma(mas R, matr D, int eil, int st ) { int i, j; for (i=0; i<eil; i++) { *(R+i) = 0; for(j=0; j<st; j++) *(R+i) += D[i][j]; } }
ia FailoVardas nurodomas kaip simboli eilut: konstanta tarp kabui, arba konstantos vardu, arba kintamuoju, turiniu t reikm. Failo paruoimo darbui bvis nurodomas antruoju parametru M (simbolin eilut: konstanta, jos vardas arba kintamasis). Galimos bvio reikms suraytos 2.1 lentelje.
2.1 lentel. Failo paruoimo darbui bsenos
r w a
tik skaitymui; tik raymui; jeigu failas egzistavo, tai naikinamas ir sukuriamas naujai; papildymui gale; jeigu failo nebuvo, tai sukuriamas;
Bvio reikm papildius raide t (rt, wt, at), failas bus paruoiamas darbui kaip tekstinis. Bvio reikm papildius raide b (rb, wb, ab), failas bus paruoiamas darbui kaip binarinis. Jeigu nebus panaudota nei t, nei b raids, tai failas bus atidaromas darbui priklausomai nuo globalinio kintamojo fmode reikms (r. fcntl.h). Kiekvienas bvio variantas 33
gali bti papildytas enklu + (plius) (pvz.: r+, w+, wt+). Tai reikia, kad failai paruoiami atnaujinimui (leidiama skaityti ir rayti). Toliau pristatome pagrindini buferizuot I/O funkcij prototipus.
int fclose(FILE *rod); int putc(int simbolis, FILE *rod); int getc(FILE *rod); int putw(int sk, FILE *rod); int getw(FILE *rod); // //
// simbolio skaitymas //
// skaiiaus skaitymas
char *fgets(char *s, int n, FILE *rod); // skaito eilut s i n simboli int fputs(const char *s, FILE *rod); //
eiluts s raymas
int fread(void *buf, int t, int n, FILE *rod); // siunia n po t bait turini blok i failo rod bufer buf;
alinamas failas
Fail apdorojimo programoms apdorojam fail vardai gali bti perduodami pagrindins funkcijos parametrais. Funkcijos prototipas:
int main(int n, char *argv[ ]);
34
ia n argument skaiius, o argv argumentams skirto eilui masyvo rodykl. Argument reikms yra nurodomos programos ikvietimo komandoje ir yra atskiriamos tarpais. Nulin argument masyvo eilut tai paios programos pavadinimas. 2.6 pratimas. Duomen faile duom6.dat surayti skaiiai. Reikia duomenis i failo surayti masyv ir atspausdinti ekrane. Suskaiiuoti masyvo element sum ir atspausdinti ekrane.
#include #include #include #include <io.h> <stdio.h> <iostream.h> <conio.h>
const Kiek = 15; typedef int mas [Kiek]; FILE *F; void Sp (mas, int); // Spausdina masyv int Suma (mas, int); // Sumuoja masyvo elementus // Pagrindin programa void main() { mas A; int n = 0; F = fopen("duom6.dat", "r"); if (F == NULL) printf("Failas neatidarytas"); else { while (!feof(F) && ( n < Kiek)) { fscanf(F, "%d", &A[n]); n ++; } Sp(A, n); printf("Suma=%5d\nPabaiga", Suma(A, n)); }
} // Spausdina masyv void Sp(mas X, int n) { int i = 0; while (i<n) cout << X[i++] << " " << endl; } // Sumuoja masyvo elementus int Suma(mas R, int k) { int i = 0, S = 0; while (i<k) S += R[i++]; return S; }
35
2.7 pratimas. Duomen faile duom7.dat surayti skaiiai. Reikia duomenis i failo surayti masyv. Masyvo element reikms atspausdinamos kitame faile.
#include #include #include #include #include <iostream.h> <stdio.h> <alloc.h> <conio.h> <stdlib.h>
const Kiek = 10; typedef int mas [Kiek]; void SpF(mas, int, char); // Masyvo element ivedimas fail int Ivesti(mas, int *); // Skaitymas i failo // Pagrindin programa void main() { mas A; int n = 0; if (Ivesti(A, &n)) { cout << "\nDuomen failo nra\n"; getch(); exit(1); } SpF(A, n, 'A'); } // Skaitymas i failo int Ivesti(mas A, int *n) { FILE *F; F = fopen("duom7.dat", "r"); if (F == NULL) { printf( "Failas neatidarytas"); return 1; } else { while(!feof(F) && (*n < Kiek)) { fscanf(F, "%d", &A[*n]); n++; } // Borland C++4.5 realizacijoje raoma (*n)++ fclose(F); return 0; } } // Masyvo element ivedimas fail void SpF(mas X, int n, char R) { FILE *F; int i = 0; F = fopen("duom7.rez", "w");
36
if (F == NULL) printf("Failas neatidarytas spausdinimui"); else { while (i<n) fprintf(F, "%c[%2d]=%4d\n", R, i, X[i++]); fclose(F); } }
Palyginkite visus tris variantus ir pasirinkite, kuris Jums suprantamiausias. Silome programas rayti paprastesnes ir aikesnes, nes jas lengviau ir greiiau suvokti ne tik Jums, po kurio laiko modifikuojant, bet ir kitiems, turintiems maiau praktinio programavimo gdi. 2.8 pratimas. Duomen faile suraytos tak, esani koordinai ploktumoje, koordinats (x, y). Reikia duomenis surayti matric, kur pirmame stulpelyje bt tak x koordinats, antrame stulpelyje y koordinats. Parayti funkcij, kuri matricos treiame stulpelyje surayt tak atstumus iki koordinai pradios tako. Ketvirtame stulpelyje paymti, kokiam ketviriui takas priklauso. Nuliuku ymti takus, esanius ant ai. Rezultat faile atspausdinti 37
matricos duomenis, uraant kiekvieno stulpelio prasm nusakanius pavadinimus ir numeruojant eilutes. Gale parayti, kiek kuriame ketvirtyje yra tak, ir kiek yra tak ant ai.
#include #include #include #include #include #include #include <iostream.h> <stdio.h> <alloc.h> <conio.h> <stdlib.h> <math.h> <time.h>
typedef float mas[][4]; const char *Duomenys = "Duom8.dat"; const char *Rezultatai = "Duom8.rez"; void RezFailas(); // int Failas(); // mas *Ivesti(int); // void Spausdinti(mas *, int, int); // void Atstumai(mas *, int); // void Vieta(mas *, int); // // Pagrindin programa void main() { mas *A; int n; RezFailas(); if((n = Failas()) == 0) { cout<<" Duomenu nerasta\n"; getch(); exit(0); } A = Ivesti(n); if (A == NULL) { cout<<"NERA NERA \n"; getch(); exit(1); } cout << "Masyvas ivestas n = "<< n Spausdinti(A, n, 0); Atstumai(A, n); Spausdinti(A, n, 1); Vieta(A, n); Spausdinti(A, n, 2); } Rezultat failo paruoimas Randa kiek yra duomen Duomen vedimas Rezultat spausdinimas Tak atstumai
Tak padtis ploktumoje
<< endl;
38
// Paruoia rezultat fail. Urao dat ir laik void RezFailas() { FILE *F; char Data[9], Laikas[9]; if(F = fopen(Rezultatai, "w")) { _strdate(Data); _strtime(Laikas); fprintf(F, "%s %s\n", Data, Laikas); fclose(F); } } // Patikrina, ar yra duomen failas ir kiek jame tak int Failas() { FILE *F; int n = 0, k; if ((F = fopen(Duomenys, "r")) == NULL ) { cout << "Duomenu failo nera\n"; return 0; } while(!feof(F)) { n++; fscanf(F, "%d%d\n", &k, &k); } fclose(F); return n; } // Skaito duomenis i failo masyv mas *Ivesti(int n) { FILE *F; mas *A; if ((F = fopen(Duomenys, "r")) == NULL) { cout << "Duomenu failas neatidarytas\n"; getch(); return NULL; } A = (mas *) malloc(n*4*sizeof(float)); if(A == NULL) { cout << "Truksta masyvui atminties\n"; getch(); return NULL; } n = 0; while(!feof(F)) { fscanf(F, "%f%f\n", &(*A)[n][0], &(*A)[n][1]); n++; } fclose(F); return A; }
39
// Spausdina duomenis ir rezultatus /* Raktas k valdo: kai 0, spausdina tik tak koordinates (duomenis); kai 1, spausdina duomenis ir tak atstumus iki koordinai pradios tako; kai 2, spausdina duomenis, atstumus ir tak vietas koordinai ploktumoje.*/ void Spausdinti(mas *C, int n, int k) { FILE *F; int i; if ((F = fopen(Rezultatai, "a")) == NULL) { cout << "Rezultatu failas neatidarytas\n"; getch(); return; } switch(k) { case 0: fprintf(F, "Duomenys\n"); fprintf(F, "*********************\n"); fprintf(F, " Nr. x y \n"); break; case 1: fprintf(F, "\nDuomenys ir atstumai iki koord. pradzios\n"); fprintf(F, "******************************\n"); fprintf(F, " Nr. x y Atstumas \n"); break; case 2: fprintf(F, "\nDuomenys, atstumai ir tasko vieta koord. plokstumoje\n");
fprintf(F, "**************************************\n"); fprintf(F, " Nr. x y Atstumas Vieta \n");
break; } for(i=0; i<n; i++) { switch(k){ case 0: fprintf(F," %3i %7.2f %7.2f \n", i+1, (*C)[i][0],(*C)[i][1]); break; case 1: fprintf(F, "%3i %7.2f %7.2f %7.2f \n", i+1, (*C)[i][0],(*C)[i][1],(*C)[i][2]); break; case 2: fprintf(F, "%3i %7.2f %7.2f %7.2f %7.2f \n", i+1, (*C)[i][0],(*C)[i][1],(*C)[i][2],(*C)[i][3]); break; }} switch(k) {
40
} // Skaiiuojami tak atstumai iki koordinai pradios tako void Atstumai(mas *D, int n) { float x, y; for (int i = 0; i<n; i++) { x = (*D)[i][0]; y = (*D)[i][1]; (*D)[i][2] = sqrt(x*x + y*y); } } // Nustatoma tako padtis koordinai ploktumoje /* 14 nurodo ketvirio numer, o 0 kad takas yra ant vienos i ai. */ void Vieta(mas *D, int n) { float x, y; for (int i=0; i<n; i++) { x = (*D)[i][0]; y = (*D)[i][1]; if ((x>0) && (y>0)) (*D)[i][3] = 1; else if ((x>0) && (y<0)) (*D)[i][3] = 4; else if ((x<0) && (y>0)) (*D)[i][3] = 2; else if ((x<0) && (y<0)) (*D)[i][3] = 3; else (*D)[i][3] = 0; } }
} fclose(F);
case 0: fprintf(F, "*********************\n"); break; case 1: fprintf(F, "******************************\n"); break; case 2: fprintf(F, "**************************************\n"); break;
41
42
2.9 pratimas. Naudodamiesi C++ aplinkos pagalbine informacija (Ctrl+F1) isiaikinkite, kaip sudaryta struktrizuoto tekstinio failo Duom9.txt papildymo programa. Failo eilutse yra saugomi duomenys apie vairiems asmenims priklausanius telefonus. Duomen elementams yra skiriamos tokios eilui atkarpos: pavard [1, 20], vardas [21, 40], telefono numeris [41,50]. Sukurkite tokios struktros fail ir patikrinkite program. Programos vykdymo pabaigos slyg isiaikinkite nagrindami jos tekst.
#include #include #include #include <string.h> <stdio.h> <stdlib.h> <conio.h>
FILE *failas; char buf[50], e1[20], e2[20], e3[10]; const char vardas[] = "Duom9.txt"; int Init (const char *, char *); void Duomenys(FILE *); void Rodyti (FILE *); // Pagrindin programa void main() { buf[0] = '\0'; if (!Init(vardas, "a")) exit(1); // Failo paruoimas darbui Duomenys(failas); // Duomenys vedami klaviatra fail fclose(failas); if (!Init(vardas,"r")) exit(1); //Failo paruoimas skaitymui Rodyti(failas); // Duomenys siuniami ekran fclose(failas);
} // Naujo failo atidarymas skaitymui/raymui arba seno papildymui int Init (const char *v, char *mode) { if ((failas = fopen(v, mode)) == NULL) { // Failo nebuvo // Naujo sukurti nepavyko if ((failas = fopen(v, "w+")) == NULL) { fprintf(stderr, "Atidaryti fail raymui/skaitymui nepavyko.\n"); return 0; } // Klaidos poymis else return 1; } // Naujai sukurtas failas else return 2; // Surastas failas ir atidarytas }
43
// Duomenys vedami klaviatra fail void Duomenys(FILE *failas) { int i; while(buf[0] != 'q') { buf[0]= '\0';
// vedimo pabaiga nurodoma eilute, kurios pirmas simbolis yra 'q' // Struktrizuotos eiluts sudarymo ir raymo ciklai
printf("Nurodykite pavarde, varda ir telefono numeri:\n"); scanf("%s %s %s", e1, e2, e3); strcat(buf, e1); for(i=strlen(buf); i<20; i++) strcat(buf, " "); strcat(buf, e2); for(i=strlen(buf); i<40; i++) strcat(buf, " "); strcat(buf, e3); strcat(buf, "\n"); if (buf[0] != 'q') fwrite(buf, strlen(buf), 1, failas); }
Sudarykite pagalbin funkcij buferinio kintamojo buf papildymui formatuotais laukais. Pakeiskite programoje blok raymo komand fwrite eilui raymo komanda fputs. Pakeiskite program taip, kad jos darb bt galima nutraukti vedus vieno simbolio eilut: "Q" arba "q". Norint tai padaryti, reikia i klaviatros perskaityti i karto vis eilut ir tik po to j skanuoti su funkcija sscanf. 2.10 pratimas. Panaudodami duomen sraut apdorojimo klas ofstream vedame duomenis matric ir atlik veiksmus (sukeiiami vietomis stulpeliai su didiausia ir maiausia matricos reikme) ivedame rezultatus.
#include #include #include #include <fstream.h> <stdio.h> <stdlib.h> <iomanip.h>
44
const num = 15; typedef int matr[num][num]; void read(char *, matr, int *); // Duomen skaitymas void print(char *, matr, int, char *); // Spausdinimas int max(matr, int); // Didiausio paieka int min(matr, int); // Maiausio paieka void swap(matr, int, int, int); // Stulpeli sukeitimas int test(char *, char *); // Fail paruoimas // Pagrindin programa void main(void) { int n; matr a; char inbuff[12], outbuff[12]; // Fail vardams saugoti // Rodykls fail vardus *duom ir *rezult cout << "Koks duomenu failo vardas?\n"; char *duom = gets(inbuff); cout << "Koks rezultatu failo vardas?\n"; char *rezult = gets(outbuff); if (test(duom, rezult)) { cout << "Klaida atidarant failus"; exit(0); } read(duom, a, &n); // Duomen skaitymas matric // Duomen spausdinimas fail print(rezult, a, n, "Pradiniai duomenys"); swap(a, n, min(a, n), max(a, n)); print(rezult, a, n, "Rezultatai"); // Rezultat spausdinimas cout << "Pabaiga\n\a"; } // Duomen skaitymas void read(char *is, matr a, int *n) { ifstream infile(is); // Duomen failo atidarymas infile >> *n; // vedama n reikm (*n)--; // Matricos vedimas for(int i=0; i<=*n; i++) for(int j=0; j<=*n; j++) infile >> a[i][j]; infile.close(); // Failo udarymas } // Spausdinimas void print(char *os, matr a, int n, char *text) { // Rezultat failas paruoiamas papildymui ofstream offile(os, ios::app);
45
} // Randamas stulpelis su didiausia reikme int max (matr a, int n) { int m = -1000, p = -1; // p - stulpelio numeris for(int i=0; i<=n; i++) for(int j=0; j<=n; j++) if (a[i][j] > m) { m = a[i][j]; p = j; } return p; } // Randama maiausia reikm int min(matr a, int n) { int m = 1000, p = -1; // p - stulpelio numeris for(int i=0; i<=n; i++) for(int j=0; j<=n; j++) if (a[i][j] < m) { m = a[i][j]; p = j; } return p; } // Sukeiiami du stulpeliai vietomis void swap(matr a, int n, int mine, int maxe) { int p; for(int i=0; i<=n; i++) { p = a[i][mine]; a[i][mine] = a[i][maxe]; a[i][maxe] = p; } } // Pagrindin programa int test(char *is, char *os) { ifstream duomenys(is); // Patikrinamas duomen failas if (!duomenys) return 1; duomenys.close(); ofstream rezultatai(os); // Patikrinamas rezultat failas if (!rezultatai) return 1; // rezultat fail ivedamas praneimas rezultatai << "Matrica \n"; rezultatai.close(); return 0;
offile << '\n' << text << '\n' << " "; for(int j=1; j<=n+1; j++) offile << setw(5) << j; offile << " \n |"; for(j=0; j<=n; j++) offile << "-----"; offile << " \n"; for(int i=0; i<=n; i++) { offile << setw(3) << (i+1) << " |"; for(int j=0; j<=n; j++) offile << setw(5) << a[i][j]; offile << " \n"; }
46
} Duomen failas 4 15 1 5 6 2 25 6 7 5 -5 5 -45 1 2 3 4 Rezultat failas Matrica Pradiniai duomenys 1 2 3 4 |-------------------1 | 15 1 5 6 2 | 2 25 6 7 3 | 5 -5 5 -45 4 | 1 2 3 4 Rezultatai 1 2 3 4 |-------------------1 | 15 6 5 1 2 | 2 7 6 25 3 | 5 -45 5 -5 4 | 1 4 3 2
47
Eil[i] -= c; }
Sudarykite program, kuri skaiiuot ir parodyt ekrane vis klaviatra vestos eiluts lotynik raidi pasikartojimo danius. Didiosios ir maosios raids turi bti interpretuojamos vienodai. Neumirkite, kad C kalboje char ir int tipai yra suderinami Prie nagrindami tolesnius pavyzdius, apvelgsime kelias funkcijas, palengvinanias darb su eilutmis.
char *strcpy(char *dest, const char *src); Eilut src kopijuoja dest iki nulinio simbolio. Grina rodykl dest. char *stpcpy(char *dest, const char *src); Eilut src kopijuoja eilut dest iki nulinio simbolio. Grina dest + strlen(src). char *strcat(char *dest, const char *src); Eiluts src kopij prijungia prie eiluts dest galo. Grina rodykl sujungtas eilutes (dest). Grinamos eiluts ilgis yra: strlen(dest) + strlen(src). const char *strchr(const char *s, int c); char *strchr( char *s, int c); Eilutje s ieko simbolio c. Paieka pradedama nuo eiluts pradios ir
baigiama suradus pirmj simbol, lyg nurodytam. Nulinis simbolis laikomas eiluts dalimi, todl jo paieka taip pat galima. Pavyzdiui, strchr(strs,0) grina rodykl nulin simbol. Funkcija grina rodykl surast simbol eilutje, arba NULL neradus. 2.12 pratimas. Programa iliustruoja funkcijos strchr veikim.
#include <string.h> #include <stdio.h> int main(void) { char string[20]; char *ptr, c = 'r'; strcpy(string, "Nagrinelama eilute"); ptr = strchr(string, c); if (ptr) printf("Simbolis %c yra: %d-as\n", c, ptr-string); else printf("Tokio simbolio eiluteje nera\n");
48
return 0; }
Pertvarkykite program taip, kad ji ivardyt ekrane visas dialogo su vartotoju metu nurodytos raids pozicijas klaviatra vestoje eilutje.
int strcmp(const char *s1, const char *s2);
Palygina eilutes. Lygina eilutes, pradedant pirmaisiais simboliais iki tol, kol bus surasti nesutampantys simboliai arba bus surasta vienos i eilui pabaiga. Jeigu s1 yra... maesn u eilut s2 lygi eilutei s2 didesn u eilut s2 strcmp grina reikm, kuri yra...
< 0 == 0 > 0
Palygina eilutes, kaip ir ankstesn, tik masias ir didisias raides laiko vienodomis.
size_t strlen(const char *s);
Kopijuoja i eiluts src eilut dest nurodyt simboli skaii maxlen. Jeigu eilutje src simboli maiau, negu nurodytas kiekis maxlen, tuomet rezultate bus tiek, kiek surasta; jeigu daugiau negu reikia, tai tiek kiek nurodyta. Grinama rodykl dest eilut. 2.13 pratimas. Programa iliustruoja funkcijos strncpy veikim.
#include <stdio.h> #include <string.h> int main(void) { char string[10]; char *str1 = "abcdefghi"; strncpy(string, str1, 3); string[3] = '\0'; printf("%s\n", string);
49
return 0; }
char *strtok(char *s1, const char *s2); Funkcija grina rodykl s1 fragment (od) i simboli, kuri nra eilutje s2, o u fragmento terpia nulinio kodo simbol. Kartojant kreipin su NULL vietoje pirmo argumento, grinama rodykl kit od. Kai nauj odi nebra, grinama rodykl NULL. 2.14 pratimas. Programa iliustruoja funkcijos strtok veikim.
#include <string.h> #include <iostream.h> #include <conio.h> void main() { char Eil[30] = "Joju, dairausi ir dainuoju"; char *p, sep[5]= ", "; cout << Eil << endl; // Visa tiriama eilut // strtok terpia ribotuv NULL u surasto odio p = strtok(Eil, sep); if (p) cout << p << endl; // Pirmasis odis // Antras kreipinys su argumentu NULL grina antro odio adres p = strtok(NULL, sep); if (p) cout << p << endl; // Antrasis odis cout << Eil << endl; // Tik pirmasis odis }
Pertvarkykite pavyzdio program su ciklo operatoriumi taip, kad ji parodyt ekrane visus tiriamos eiluts odius. Analizs ciklo pabaigos poymiu vartokite grinamos rodykls reikm NULL. Atskiriamus i 50
Kompiuterio laik urao eilutje buf, kurios ilgis privalo bti 9 Eiluts forma HH:MM:SS, kur HH valandos, MM minuts ir SS sekunds. Grina eiluts buf adres. Eilut baigiama nuliniu simboliu.
char *_strdate(char *buf);
Kompiuterio dat urao eilutje buf, kurios ilgis privalo bti 9 Eiluts forma MM/DD/YY, kur MM mnuo, DD mnesio diena ir YY met paskutiniai du skaiiai. Grina eiluts buf adres. Eilut baigiama nuliniu simboliu. 2.15 pratimas. Programa iliustruoja funkcij _strdate veikim.
#include <time.h> #include <stdio.h> void main(void) { char data[9]; char laikas[9]; _strdate(data); _strtime(laikas); printf("Data: %s Laikas: %s\n", data, laikas); }
ir _strtime
2.16 pratimas. Klaviatra vedama simboli eilut, kur odiai skiriami bent vienu tarpu. Reikia atspausdinti eiluts odius po vien ekrane, nurodant odio pradios ir pabaigos indeksus. Ankstesniame pratime eiluts vedimo pabaiga buvo pirmas sutiktas tarpas arba eiluts pabaiga, todl paraius kelis odius, buvo vedamas tik pirmasis. Visos eiluts vedimui galima naudoti funkcij: #include <stdio.h> char *gets(char *s);
51
Skaitomos eiluts pabaigos simbolis '\n' automatikai pakeiiamas simboliu '\0'. Sudarant program, tikslinga vis eilut perskaityti jai skiriam simboli masyv ir po to analizuoti po vien simbol.
#include #include #include #include <iostream.h> <conio.h> <stdio.h> <string.h>
typedef char Mas[80]; void zodis(Mas, Mas, int *); // odio atskyrimas // Pagrindin programa void main() { Mas Eil, Z; int i, c; cout << "veskite eilut maosiomis raidmis\n"; cout << " odius atskirkite tarpais\n"; gets(Eil); // Simboli eilut cout << Eil << endl; // Eilut spausdinama ekrane strcat(Eil, " "); // Po paskutinio odio bus tarpas i = 0; while(Eil[i] != '\0') { // odi atskyrimas: c = i; // odio pradia; zodis(Eil, Z, &i); // surastas odis pradedant i vieta; if (Z[0] != '\0') // jeigu buvo odis, tai spausdinamas cout << "Pradia: " << c << " Pabaiga: " << (i-1) << " *" << Z << "*" << endl; i++; } // Praleidiamas tarpas } // odio atskyrimas /* I eiluts A, pradedant simboliu, numeris n, iskiriamas odis. Surasto odio simboliai suraomi eilut B. Gale nulinis simbolis */ void zodis(Mas A, Mas B, int *n) { int i = 0; while((A[*n] != '\0') && (A[*n] != ' ')) B[i++] = A[(*n)++]; B[i] = '\0'; }
52
Skaitant i klaviatros, turi bti vartojamas failo vardas stdin. Eiluts skaitymo bufer apraymo pavyzdys:
// Tuias ciklas! while((Eil[i++] = getc(stdin)) != '\n'); str[--i]='\0'; // Eiluts pabaigos ym
Pertvarkykite odi atskyrimo program taip, kad ji ekrane parodyt tik ilgiausi od. odio ilgio skaiiavimui siloma vartoti toki funkcij:
int length(char* x) { int i = 0; while(x[i++]); return --i; }
Atskiro simboli eilui tipo C++ kalboje nra. Eiluts yra saugomos simboli masyvuose, kur j pabaigos yra ymimos nulinio kodo simboliais '\0'. Daugumoje C++ kalbos realizacij yra toki masyv apdorojimo bibliotekos, kuriose yra vairios eilui struktros analizs ir j tvarkymo funkcijos. Borland C++ realizacijoje ios priemons yra paskirstytos dviejose bibliotekose: string.h (struktros analiz ir tvarkymas) ir stdlib.h (tip keitimas). 2.17 pratimas. Savarankikai isiaikinkite pavyzdio programlje vartojam eilui tvarkymo funkcij paskirtis ir kreipimosi jas bdus.
#include <iostream.h> #include <string.h> #include <conio.h> typedef char zodis[15]; typedef char string[80]; int main() { zodis x; string z, t; cout << "Iveskite savo varda ir cin >> z >> x; strcat(z, " "); strcat(z, x); strcpy(t, z); strlwr(t); cout << "Mazosiomis raidemis: " strupr(t);
pavarde\n";
// Eilui sujungimas
53
cout << "Didziosiomis raidemis: " << t << endl; cout << "Buvo ivesta: " << z << endl; cout << "Buvo raidziu: " << (strlen(z)-1) << endl; }
Pertvarkykite program taip, kad ji i klaviatra vestos eiluts atskirt odius ir paskirstyt du masyvus: skaii ir kitoki odi. Abiej masyv elementai ir skaii masyvo suma turi bti parodomi ekrane. Eilui pertvarkymui skaiius vartokite bibliotekos stdlib.h funkcijas, kuri prototipai: double atof(const char *s); int atoi(const char *s); Jos grina skaii reikmes arba 0, jeigu argumento eiluts negalima pertvarkyti skaii.
54
3 skyrius. Struktros
Struktr apra sintaks:
struct <struktrinio tipo vardas> { <lauk apra sraas> } [<kintamj sraas>];
Lauk apra sra elementai atskiriami kabliatakiais, o kintamj srao elementai kableliais. Jei aprae nra kintamj srao, jis apibria tik nauj struktrini duomen tip, taiau jo realizacijoms atmintyje vietos neskiria. Atskiro struktros tipo apibrimo ir realizavimo pavyzdys:
// Apibrimas struct telefonas { char vardas[30]; unsigned longint tel; // Realizavimas struct telefonas Tel, *P, TelMas[100]; <struktros vardas>.<lauko vardas>
Kreipiantis struktr elementus vartojami sudtiniai vardai: Galimos rodykls sruktr tipo reikmes:
P = &Tel; // struct telefonas *P;
55
FILE *F; const char Duomenys[]="Prat31.rez"; int Atidaro(const char *, char *); void Lentele(); // Pagrindin programa void main(void ) { if (Atidaro(Duomenys, "w")) exit(0); Lentele(); fclose(F); cout << "Pabaiga\n\a"; } // Failo atidarymas int Atidaro( const char *Vardas, char *Forma){ if (( F = fopen( Vardas, Forma )) == NULL ) { cout<< "Failas neatidarytas"; return 1;} else return 0; } // Duomenys vedami klaviatra ir spausdinami faile void Lentele() { struct zmogus A; int n; cout << "Kiek zmoniu bus ?\n"; cin >> n;
fprintf(F,"..........................................\n"); fprintf(F,": Vardas : Pavarde : gim. m. : ugis :\n"); fprintf(F,"..........................................\n");
while(n--) { cout << "Iveskite varda \n"; cin >> A.vardas ; cout << "Iveskite pavarde \n"; cin >> A.pavarde; cout << "Iveskite gimimo metus \n"; cin >> A.gim_met; cout << "Iveskite ugi \n"; cin >> A.ugis; fprintf(F, ":%10s :%10s : %4d : %3.2f :\n", A.vardas, A.pavarde, A.gim_met, A.ugis); }
fprintf(F,"..........................................\n");
fclose(F);
} .......................................... : Vardas : Pavarde : gim. m. : ugis : .......................................... : Petras : Petraitis : 1933 : 2.15 : : Jurgis : Jurgelis : 1582 : 1.58 : : Kazys : Kazelis : 1258 : 1.25 : ..........................................
56
Struktr masyvai sudaromi taip pat, kaip ir paprast duomen tip. Rekomenduojama sukurti duomen tip, po to j naudoti. 3.2 pratimas. Klaviatra vedami duomenys suraomi struktr masyve. Po to jie spausdinami lentele.
#include #include #include #include <stdio.h> <stdlib.h> <iomanip.h> <conio.h>
struct zmogus { char vardas [10]; char pavarde[10]; int gim_met; float ugis; }; FILE *F; const char Duomenys[]="Prat32.rez"; int Atidaro(const char *, char *); void Ivesti(zmogus *, int *); void Lentele(zmogus *, int); // Pagrindin programa void main(void ) { zmogus A[10]; int n = 0; Ivesti(A, &n); if (Atidaro(Duomenys, "w")) exit(0); Lentele(A, n); fclose(F); cout<<"Pabaiga\n\a"; } // vedimas klaviatra void Ivesti(zmogus *B, int *n) { int i = 0; cout << "Kiek zmoniu bus ?\n"; cin >> *n; while(i < *n) { cout << "*********************\n"; cout << (i+1) << "-ojo zmogaus duomenys:\n"; cout << "Iveskite varda \n"; cin >> B[i].vardas; cout << "Iveskite pavarde \n"; cin >> B[i].pavarde; cout << "Iveskite gim. metus\n"; cin >> B[i].gim_met; cout << "Iveskite ugi \n"; cin >> B[i].ugis; cout << "Aciu \n"; i++; } }
57
// Failo atidarymas int Atidaro(const char *Vardas, char *Forma) { if ((F = fopen(Vardas, Forma)) == NULL ) { cout << "Failas neatidarytas"; return 1; } else return 0; } // Ivedimas lentele void Lentele(zmogus *C, int n) { int i = 0;
fprintf(F,"..........................................\n"); fprintf(F,": Vardas : Pavarde : gim. m. : ugis :\n"); fprintf(F,"..........................................\n");
while(i < n) { fprintf(F, ":%10s :%10s : %4d : %3.2f :\n", C[i].vardas, C[i].pavarde, C[i].gim_met, C[i].ugis); i++; }
fprintf(F,"..........................................\n");
fclose(F); }
3.3 pratimas. Klaviatra vedami duomenys suraomi tipizuot fail. Po to jie skaitomi i to failo ir parodomi ekrane.
#include #include #include #include <stdio.h> <stdlib.h> <iomanip.h> <conio.h>
FILE *failas; const char Rezultatai[]="Prat33.rez"; struct telefonas { char vardas [10]; char pavarde[10]; long tel; } T; int Atidaro ( const char *, char *); void Raso(); void Skaito(); // Pagrindin programa void main(void ) { if (Atidaro(Rezultatai, "w")) exit(0); Raso(); fclose(failas); if (Atidaro(Rezultatai, "r")) exit (0); Skaito();
58
} // vedamus duomenis surao fail void Raso() { char buf[50] = ""; printf("Programos nutraukimas - eilute 'q'\n"); while(buf[0] != 'q') { printf("Nurodykite varda, pavarde ir telefono numeri:\n"); gets(buf); if (buf[0] != 'q') { sscanf(buf, "%s%s%ld", T.vardas, T.pavarde, &T.tel); fwrite(&T, sizeof(struct telefonas), 1, failas); } } } // Failo atidarymas int Atidaro(const char *Vardas, char *Forma) { if ((failas = fopen(Vardas, Forma)) == NULL) { cout << "Failas neatidarytas"; return 1; } else return 0; } // Skaito i failo void Skaito() { printf(".......................................\n"); printf(": Vardas : Pavarde : telefonas :\n"); printf(".......................................\n"); while(fread(&T, sizeof(struct telefonas), 1, failas)) printf( ":%10s :%10s : %10ld :\n", T.vardas, T.pavarde, T.tel); printf(".......................................\n"); }
Pertvarkykite program, kad pagrindinje funkcijoje pagal vartotojo pageidavimus bt galima pasirinkti tokius veiksmus: duomen failo turinio ivedimas ekrane, naujo failo formavimas arba esanio failo papildymas. Programos akojimui vartokite operatori case. Papildykite program failo rikiavimo pagal pavardes alfabeto tvarka funkcija. Rikiuojamo failo duomenys i pradi turi bti perraomi ra masyv ir tik po to rikiuojami. Lyginant eilutes turi bti vartojamos ne santykio operacijos, bet bibliotekos string.h funkcijos arba makrokomandos: stricmp, strnicmp, strncmpi, strcmpi.
59
Kuriant sudtingesnes programas tikslinga inoti kelet domesni funkcij savybi. Kalboje C++ realizuota labai efektyvi funkcij savyb polimorfizmas, kuri dar vadinama daugiavariantikumu arba funkcij persidengimu (overloading). Senose C++ kalbos versijose polimorfini funkcij apraai turi bti paymimi baziniu odiu overload. Kalbos realizacijose Borland C++ tai daryti nebtina. 3.4 pratimas. Demonstruojama ivedimo ekrane polimorfin funkcija print, kurios darbas priklauso nuo kreipinyje urayto duomen tipo.
#include <stdio.h> #include <conio.h> // overload inline void inline void inline void print; // Bordland C galima praleisti. print(int x) {printf("%d\n" , x); } print(double x) {printf("%5.2f\n", x); } print(char *x) {printf("%s\n", x); } // Slankaus kablelio skaiius // Sveikas skaiius // Simboli seka
ia baziniu odiu inline paymtos terpiamos funkcijos. terpiamos funkcijos yra makrokomand analogai pirminis procesorius rao funkcijos tekst kiekvieno kreipinio j vietoje. Jei funkcij tekstai trumpi, toks funkcij tekst terpimas padidina program darbo greit. 3.5 pratimas. Dar viena labai naudinga funkcij savyb argument parinkimas pagal nutyljim.
#include <stdio.h> #include <conio.h> struct upe { char *vardas; char *salis; } x, y, z, k; // Funkcija Rodo void Rodo(struct upe *d, char *a = NULL, char *b = NULL) { d->vardas = a; d->salis = b; } // Pagrindin programa void main(void) { Rodo(&x);
60
Rodo(&y, "Nemunas"); Rodo(&z, "Merkys", "Lietuva"); Rodo(&k, "Nilas"); printf("%10s %10s \n", x.vardas, printf("%10s %10s \n", y.vardas, printf("%10s %10s \n", z.vardas, printf("%10s %10s \n", k.vardas, }
61
Klasje duomenys ir metodai gali bti raomi bet kokia seka. Klass elementai (duomenys ir metodai) gali turti poymius. Poymis klasje galioja tol, kol bus sutiktas kito poymio uraas. Jeigu poymio urao nra, tuomet pagal nutyljim bus priimtas private visiems elementams iki pirmojo poymio urao, jeigu jis bus. Yra tokie poymiai: private (lokalusis). Elementai prieinami tik klass viduje. public (globalusis). Klass elementai prieinami jos iorje. protected (apsaugotasis). Klass elementai prienami klasje, kuri paveldi duotj klas. ia jie galioja private teismis.
Programavimo technologijos poiriu reikt laikytis tam tikros tvarkos. Rekomenduojama pradioje surayti duomenis, po to metodus. Metod srae taip pat reikalinga tvarka. Pirmuoju srae turt bti klass konstruktorius. Tai metodas, skirtas klass objekto pradini duomen reikmms nurodyti. Jo vardas privalo sutapti su klass pavadinimu. Konstruktorius neturi grinamos reikms. Toliau metod srae turi bti darbo su duomenimis metodai. Gale raomas destruktorius, jeigu toks yra. Tai metodas, naikinantis objekt. Destruktoriaus vardas turi sutapti su klass vardu, kurio pradioje paraomas simbolis ~. Pavyzdiui: 62
// Konstruktorius // Destruktorius
Konstruktorius ir destruktorius privalo bti public. Konstruktorius, kuris neturi parametr, ikvieiamas darbui pagal nutyljim. Galima parayti:
Katinas A; // vietoje Katinas A();
Destruktorius skirtas tos klass objektui naikinti kompiuterio atmintyje. Jeigu jo nra, objektas naikinamas baigus vykdyti program. Programos darbo eigoje kreipinys destruktori naikina objekt. Jeigu objekto duomen laukai gauna atmint dinamikai, tuomet btina destruktoriuje parayti veiksmus, kuriais atsisakoma atminties, skirtos duomen laukams. Destruktoriai, kaip ir konstruktoriai, negrina jokios reikms. Jeigu klas turi destruktori, bet nra kreipinio j, tuomet, pagrindinei funkcijai baigus darb, jis yra vykdomas pagal nutyljim. Metodai klasje gali bti pilnai aprayti. Tokius metod apraus tikslinga turti, jeigu j tekstas yra trumpas. Kit metod apraai ikeliami u klass rib. Tuomet klasje raomas tik metodo prototipas. Metodo aprao klass iorje struktra:
<Grinamos reikms tipas> <Klass Vardas>:: <Metodo vardas> (<Parametr sraas>) { <Programos tekstas> }
Klass tipo kintamieji vadinami objektais. J apraymas analogikas kit tip kintamj apraams. Sukuriami objektai gali bti statiniai, dinaminiai. Galima turti objekt masyvus. Objektai gali bti dinamini sra elementais. 4.1 pratimas. Sukuriama klas, apraanti vieno studento savyb svor. Klasje yra du metodai: duomen skaitymo klaviatra ir duomen rodymo ekrane. Pagrindinje funkcijoje sukuriami du objektai. Duomenys vedami panaudojant metod Skaito, o parodomi ekrane panaudojant metod Rodo. Metodai klasje apraomi kaip public, nes juos naudojame klass iorje.
#include <iostream.h> #include <conio.h> // vedimo/ivedimo priemons // Ryio su ekranu priemons
63
// Klass apraymas class Studentas { char *pav; float svoris; public: void Skaito() { // Metodas cout << "Iveskite pavarde: \n"; cin >> pav; cout << "Iveskite svori:\n"; cin >> svoris; }; void Rodo() { // Metodas cout << pav << " " << svoris << endl; }; }; // Pagrindin programa void main() { Studentas A, B; // Sukuriami objektai A.Skaito(); B.Skaito(); B.Rodo(); A.Rodo(); }
4.2 pratimas. Sukuriama klas studentas, kuri turi du duomen laukus, skirtus saugoti pavardei ir svoriui. Konstruktorius studentas suteikia pradines reikmes duomen laukams. Reikms nurodomos objekto sukrimo metu. Konstruktoriaus ir metodo Rodo apraai ikelti u klass rib. Metodas Rodo iveda ekrane studento pavard ir svor. terpiami metodai RodoVarda ir RodoSvori skirti gauti informacij apie duomen laukuose saugom pavard ir svor. Metodas Duomenys skirtas nauj duomen perdavimui objekt. Vidini metod informacijai apdoroti klasje nra. Klass objektai skiriami tik duomen registracijai.
#include <stdio.h> #include <stdlib.h> #include <iostream.h> #include <conio.h> // Klass apraymas class student { char *vardas; float svoris; public:
64
}; // Pagrindin programa void main() { clrscr(); student X("Petraitis", 13.5); // Objektas X student Y(" ", 0); // Objektas Y X.Rodo(); // Objekto X duomenys cout << "Objekto student X duomenys \n"; cout << X.RodoVarda() << " : "<< X.RodoSvori(); cout << endl; // Duomen persiuntimas kitam objektui, t.y. Y = X Y.Duomenys(X.RodoVarda(), X.RodoSvori()); Y.Rodo(); // Objekto Y duomenys
student(char *, float); void Rodo(); char *RodoVarda() { return vardas; } float RodoSvori() { return svoris; } void Duomenys(char *a, float b) { vardas = a; svoris = b; }
// // // // //
} // Metod apraai student::student(char *a, float b) { vardas = a; svoris = b; } void student::Rodo() { cout << vardas << " : " << svoris << endl; }
4.3 pratimas. Sukuriama klas darbui su skaiiais masyve. Numatyti veiksmai: papildyti sra nauja reikme, paalinti nurodyt reikm ir perirti ekrane turimus srao skaiius. Klas nagrinti ir panaudoti paprasiau, kai metod apraai pateikiami atskirai. Metod apraus siloma rayti tuoj po klasi (arba kiekvienos klass) apra.
#include <iostream.h> #include <conio.h> const dydis = 50;
65
// Klass apraymas class sar { int A[dydis]; int ilg; public: void sar(); // Konstruktorius void add(int); // Srao papildymo metodas void del(int); // Reikms alinimo metodas void get(); // Srao rodymo ekrane metodas }; // Metod apraai void sar::sar() { ilg = 0; } // Konstruktorius void sar::add(int naujas) { // Papildymo metodas if (ilg == dydis) { // Sraas papildomas, jeigu srae dar nebuvo cout << "sarasas pilnas"; return; } for(int i=0; i<ilg; i++) if (A[i] == naujas) return; A[ilg++] = naujas; } void sar::del(int elem) { // alinimo metodas for(int i=0; i<ilg; i++) if (A[i] == elem) { for(int j=i; j<ilg-1; j++) A[j] = A[j+1]; ilg--; } } void sar::get() { // Ivedimo ekrane metodas for(int i=0; i<ilg; i++) cout << A[i] << " "; cout << endl; } // Pagrindin programa void main() { clrscr(); sar Rasa; // Objekto apraas Rasa.sar(); // Objektas paruoiamas Rasa.add(10); // Srao papildymai Rasa.add(20); Rasa.add(30); Rasa.get(); // Ekrane matome: 10 20 30 Rasa.del(20); // aliname Rasa.get(); // Ekrane matome: 10 30 }
66
Ivedant duomenis nurodytu formatu nepanaudotos pozicijos upildomos tarpo simboliais. Metodas fill(int) leidia pakeisti upildymo simbol norimu. Pakeitimas galioja iki naujo nurodymo. 67
5.4 pratimas. Metodas put programose danai vartojamas kartu su funkcija int toupper(int), kuri maj raid keiia didija. Kiti simboliai nekinta.
#include <iostream.h> #include <conio.h> #include <ctype.h>
68
main() { char sim; char zodis[] = "Katinas ir Pele"; for(int i=0; zodis[i]; i++) { sim = toupper(zodis[i]); cout.put(sim); } cout << endl << zodis << endl; }
5.5 pratimas. vedimo klasje metodas get skirtas vieno simbolio vedimui klaviatra.
#include <iostream.h> #include <conio.h> #include <ctype.h> main() { char sim; cout << "Ar dar dirbsite?( T/N ): "; do { sim = cin.get(); sim = toupper(sim); } while((sim != 'T') && (sim != 'N')); cout << "Jus pasirinkote : " << sim << endl; }
5.6 pratimas. vedimo klasje metodas getline skirtas vienos eiluts vedimui klaviatra. Matome, kad paprastai vedama eilut iki pirmojo tarpo simbolio arba iki galo, jeigu tarpo simbolio nebuvo. Panaudoj getline metod galima vesti norim simboli skaii: nurodomas skaiius. Jeigu tiek simboli nebuvo, vedami visi simboliai iki eiluts galo. vedama eilut visuomet papildoma nuliniu simboliu (eiluts galas). 69
#include <iostream.h> #include <conio.h> #include <ctype.h> main() { char A[30]; int n; cout << "Iveskite eilute:\n"; cin >> A; // Pirmasis eiluts odis cout << "Eilute: *" << A << "*\n"; cin.getline(A, 12); //Vienuolika simboli ir '\0' cout << "Eilute: *" << A << "*\n"; cin.getline(A, sizeof(A)); // Iki galo arba tiek, kiek telpa cout << "Eilute: *" << A << "*\n"; n = cin.gcount(); // Eiluts ilgis su nuliniu simboliu cout << "n= " << n << endl; }
5.7 pratimas. vedimo klasje metodas getline gali turti trei parametr, nurodant simbol, kuriuo baigiamas vedimas. Pavyzdyje parayta raid Z. Jeigu vedamoje eilutje jos nebus, tuomet bus vedami visi eiluts simboliai arba tik tiek, kiek nurodyta, jeigu eilutje j yra daugiau.
#include <iostream.h> #include <conio.h> #include <ctype.h> main() { char A[30]; cout << "Iveskite eilute:\n"; cin.getline(A, sizeof(A), 'Z'); cout << "Eilute: *" << A << "*\n"; }
70
5.9 pratimas. Duomen failo Duom59.txt perraymas eilutmis nauj fail Rez59.txt.Taip programikai gaunama failo kopija. Metodas eof skirtas failo pabaigai nustatyti: grina reikm 0, kai failo pabaiga dar nepasiekta, ir 1, kai jau turime failo pabaig.
#include <iostream.h> #include <fstream.h> #include <conio.h> main() { ofstream R("Rez59.txt");
71
ifstream D("Duom59.txt"); char A[80]; while(!D.eof()) { D.getline(A, sizeof(A)); cout << A << endl; R << A << endl; } }
5.10 pratimas. Duomen failo Duom510.txt perraymas odiais nauj fail Rez510.txt. Ankstesnje programoje pakeitus skaitymo i failo metod operatoriumi >>, gausime rezultat faile atskirus duoto teksto odius.
#include <iostream.h> #include <fstream.h> #include <conio.h> main() { ofstream R("Rez510.txt"); ifstream D("Duom510.txt"); char A[80]; while(!D.eof()) { D >> A; cout << A << endl; R << A << endl; } }
72
5.11 pratimas. Duomen failo perraymas simboliais nauj fail Rez511.txt ir ekran. Ankstesnje 5.9 pratimo programoje pakeitus skaitymo i failo metod get, gausime rezultat faile ir ekrane duomen failo kopij.
#include <iostream.h> #include <fstream.h> #include <conio.h> main() { ofstream R("Rez511.txt"); ifstream D("Duom511.txt"); char sim; while(!D.eof()) { sim = D.get(); cout << sim; R << sim; } D.close(); R.close(); }
Dirbant su duomen failais reikia patikrinti ar failas buvo surastas ir skmingai atidarytas. Kiekvienu skaitymo i failo ingsniu btina patikrinti, ar veiksmas buvo skmingai atliktas. Tam skirtas metodas fail(), kurio rezultatas yra 0, kai klaid nebuvo, ir 1, kuomet vyko trikis. Klaid praneimams ekrane skirtas ivedimo srautu objektas cerr. Metodas fail() naudojamas ivedime, norint sitikinti, ar veiksmas buvo skmingas (buvo vietos naujam raui). 5.12 pratimas. Duomen failo Duom512.txt.skaiiai perraomi nauj fail Rez512.txt.
#include <iostream.h> #include <fstream.h> #include <conio.h> main(){ ofstream R("Rez512.txt"); ifstream D("Duom512.txt"); int a; if (D.fail()) cerr << "Neatidarytas duomenu failas"; else { while((!D.eof()) && (!D.fail())) {
73
D >> a; if(!D.fail()) { R << a << endl; cout << a << endl; } } } D.close(); R.close(); } "Duom.dat" 15 12 45 -56 15 89 "Rez13.rez" 3 15 12 45 -56 3 15 89
5.13 pratimas. iose klasse yra write ir read metodai, skirti duomen mainams su failais bait lygyje. Programa urao faile duot struktr, o po to perskaito ir parodo ekrane. Metoduose uraas (char *) nusako kompiliatoriui, kad bus naudojama rodykl skirtingus tipus.
#include <iostream.h> #include <fstream.h> #include <conio.h> struct Studentas{ char pav[20]; int metai; float ugis; }; main() { Studentas A; Studentas S = {"Petriukas ", 21, 187.5}; ofstream R("Rez513.txt"); R.write((char *) &S, sizeof(Studentas)); R.close(); ifstream D("Rez513.txt"); D.read((char *) &A, sizeof(Studentas)); cout << A.pav << A.metai << " " << A.ugis << endl; D.close(); }
74
5.14 pratimas. Analogikai galima sukurti baitin fail struktriniams duomenims i tekstinio failo saugoti. Programa demonstruoja duomen failo Duom514.txt perraym fail Rez514.txt ir po to skaitym i jo ir duomen raym ekrane.
#include <iostream.h> #include <fstream.h> #include <conio.h> struct Studentas{ char pav[20]; int metai; float ugis; }; main() { char sim; Studentas A; ofstream R("Rez514.txt"); ifstream D("Duom514.txt"); while(!D.eof()) { // Skaitomi duomenys D.getline(A.pav, sizeof(A.pav)); D >> A.metai >> A.ugis; sim = ' '; // Eiluts pabaiga while((!D.eof()) && (sim != '\n')) { sim = D.get(); cout << sim; } // Duomenys ekrane cout << A.pav << " " << A.metai << " " << A.ugis; cout << endl; // Duomenys faile R.write((char *) &A, sizeof(Studentas)); } D.close(); R.close(); // Skaitomi failo duomenys ir parodomi ekrane ifstream C("Rez15.txt"); while(!C.eof()) { C.read((char *) &A, sizeof(Studentas)); if (!C.fail()) cout << A.pav << A.metai << " " << A.ugis; cout << endl; } C.close(); }
75
Duomen failas:
Katinas Batuotas Rapolas Didysis Barbora Bulvyte 45 15 25 12.5 159.98 199.45
76
Klas B, kuri paveldi klas A, vadinama protviu (bazin klas). Klas A, kuri alia savo duomen ir metod paveldi kit klas B, vadinama palikuonimi (ivestin klas). Protvio duomenys ir metodai paveldimi kaip private, t.y. juos gali naudoti tik palikuonio metodai. Jeigu norima tiesiogiai kreiptis i iors protvio metodus, reikia nurodyti paveldjimo atribut public. 6.1 pratimas. Sukuriama klas Langas, skirta tekstinio ekraninio lango parametrams saugoti ir langui ekrane formuoti. Sukuriama klas Trys, skirta duomenims saugoti, keisti ir demonstruoti ekraniniame lange. i klas paveldi klas Langas. Jos metodai naudojami tik savo klass viduje.
#include <iostream.h> #include <conio.h> #include <stdio.h> // Klas Langas class Langas { int x1, y1, x2, y2; // Lango koordinats int fonas; // ir fono spalva public: Langas(int, int, int, int, int); void Ekranas(); };
77
// Klass Langas metodai Langas::Langas(int xv, int yv, int xa, int ya, int spalva) { x1 = xv; y1 = yv; x2 = xa; y2 = ya; fonas = spalva;} void Langas::Ekranas() { window(x1, y1, x2, y2); textbackground(fonas); clrscr(); } // Klas Trys class Trys: Langas { int a; float b; char c; public: trys(int, float, char, int, int, int, int, int); void add (int k) { a += k; } // Didina a void addf(float k) { b += k; } // Didina b void addc(char k) { c += k; } // Didina c void Rodo(); // Rodo ekrane }; // Klass Trys metodai Trys::trys(int r1, float r2, char r3, int xv, int yv, int xa, int ya, int spalva): Langas(xv, yv, xa, ya, spalva) { a = r1; b = r2; c = r3; Ekranas(); } void Trys::Rodo() { cprintf("Grazu:"); cprintf(" %5i %5.2f %3c \r\n", a, b, c); } // Pagrindin programa main() { int Spalva = 2; window(1, 1, 80, 25); textbackground(BLACK); clrscr(); Trys A(5, 2.5, 'b', 10, 10, 50, 15, BROWN); textcolor(Spalva++); textcolor(Spalva++); A.Rodo(); A.Rodo(); A.add (10 ); A.addf(25.3);
78
A.Rodo(); A.Rodo();
A.addc(3
);
6.2 pratimas. Sukuriama klas Pirmas, kuri paveldi antroji klas Antras. Pagrindinje funkcijoje sukuriami kiekvienos klass objektai. Demonstruojamas kreipinys protvio klass metod. Kad galima bt taip kreiptis, btina nurodyti paveldjimo atribut public. Konstruktoriuje Antras kreipinys konstruktori Pirmas raomas tuoj po antrats. Jis paveldimam laukui x suteikia reikm 5.
#include <iostream.h> // Paveldimo metodo panaudojimas #include <conio.h> // pagrindinje funkcijoje // Klas Pirmas class Pirmas { int x; public: Pirmas(int a) { x = a; } void Rodo() { cprintf("Pirmas::rodo() %5d \n\r", x); } }; // Klas Antras class Antras: public Pirmas { int y; public: Antras(int b):Pirmas(5) { y = b; } void Rodo(){ cprintf("Antras::rodo() %5d \n\r", y); } }; // Pagrindin programa void main() { window(1, 1, 80, 25); textbackground(BLACK); clrscr(); window(10, 10, 40, 18); textbackground(GREEN); clrscr();
79
window(13, 12, 37, 16); textcolor( BLACK ); Pirmas A(10); A.Rodo(); Antras B(25); B.Rodo(); B.Pirmas::Rodo(); getch(); }
6.3 pratimas. Demonstruojamas hierarchinis klasi paveldjimas. Sukuriamos trys klass: ymeklio valdymo Vieta, raids raymo ekrane nurodytoje vietoje nurodyta spalva Raide ir odio raymo ekrane po vien raid Zodis. Klas Vieta ymeklio nevaldo. Ji skirta ymeklio koordinatms saugoti ir keisti.
#include <iostream.h> #include <conio.h> #include <stdio.h> #include <string.h> // Klas Vieta class Vieta { int x, y; public: Vieta(int Xs, int Ys) { x = Xs; y = Ys; } int KoksX() { return x; } int KoksY() { return y; } void Kitas(int Xs, int Ys) { x = Xs; y = Ys; } }; // Klas Raid class Raide: Vieta { int spalva; char sim; public: Raide(int Xp, int Yp,int Sp, char Simb): Vieta(Xp, Yp) { spalva = Sp; sim = Simb; } void Kita(int a, int b, int c, char s) { Kitas(KoksX()+a, KoksY()+b); spalva +=c ;
80
sim =s; } void Rodo() { textcolor(spalva); gotoxy(KoksX(), KoksY()); cprintf("%c", sim ); } }; // Klas Zodis class Zodis: Raide { char Zd[]; public: Zodis(char A[]):Raide(1, 1, 1, 'A') { strcpy (Zd, A); } void Spausd(); }; void Zodis::Spausd() { int i = 0; while(Zd[i] != '\0') { Kita(1, 1, 1, Zd[i]); Rodo(); i++; } } // Pagrindin programa void main(){ window(1, 1, 80, 25); textbackground(BLACK); clrscr(); Raide A(10, 2, RED, 'G'); A.Rodo(); A.Kita(2, 1, 1, 'R'); A.Rodo(); Zodis B("Kaunas\0"); B.Spausd(); getch(); }
81
6.4 pratimas. Demonstruojamas paveldimo metodo panaudojimas. Klas Zodis turi metod Rodo. Klas Raide taip pat turi metod Rodo, kuri paveldi Zodis. Sukurtas objektas Zodis B; B.Rodo(); kreipinys savo klass objekt, o B.Raide::Rodo(); kreipinys paveldtos klass objekt. Klas Zodis turi paveldti klas Raide su nuoroda public.
#include <iostream.h> #include <conio.h> #include <stdio.h> #include <string.h> // Klas Vieta class Vieta { int x, y; public: Vieta(int Xs, int Ys) { x = Xs; y = Ys; } int KoksX() { return x; } int KoksY() { return y; } void Kitas(int Xs, int Ys) { x = Xs; y = Ys; } }; // Klas Raide class Raide: Vieta { int spalva; char sim; public: Raide(int Xp, int Yp, int Sp, char Simb): Vieta(Xp, Yp) { spalva = Sp; sim = Simb; } void Kita(int a, int b, int c, char s) { Kitas(KoksX()+a, KoksY()+b); spalva +=c ; sim =s; } void Rodo() { textcolor(spalva); gotoxy(KoksX(), KoksY()); cprintf("%c", sim); } }; // Klas Zodis class Zodis: public Raide { char *Zd; public: Zodis(char *A):Raide(1, 1, 1, 'A') { Zd = A ; } void Rodo(); };
82
// Zodis metodai void Zodis::Rodo() { // Metodo apraymas int i =0; while(*(Zd+i) != '\0') { Kita(1, 1, 1, *(Zd+i)); Raide::Rodo(); i++; } } // Pagrindin programa void main() { char *Vardas ="Lapas"; window(1, 1, 80, 25); textbackground(BLACK); clrscr(); Raide A(10, 3, RED, 'G'); A.Rodo(); // Ekrane raid G A.Kita(2, 2, 1, 'R' ); A.Rodo(); // Ekrane raid R Zodis B(Vardas); B.Raide::Rodo(); // Ekrane raid A B.Rodo(); // Ekrane odio Lapas raids getch(); }
G R
83
(overloading). Operatori apraai nuo funkcij skiriasi vartojamu odiu operator: operator <Operatoriaus simbolis>; Perdengimas draudiamas operatoriams: . .* :: ?: 6.5 pratimas. Demonstruojamas dviej operatori + ir perkrovimas klass Katinas objektams. Operatoriumi + bus sudedamos dvi eiluts, kuri viena yra objekte, o kita nurodoma parametru. Rezultate objekto eilut pailgja. Kitas operatorius skiriamas nurodyto simbolio vis pakartojim objekto eilutje alinimui.
#include <iostream.h> // Operatori perkrovimas #include <conio.h> #include <string.h> const N = 40; // Klas Katinas class Katinas { public: Katinas(char *); // Konstruktorius void operator + (char *); void operator - (char ); void Rodo(); ~Katinas(); // Destruktorius private: char *Vardas; }; // Katinas metodai Katinas::Katinas(char *Vardas) { Katinas::Vardas = new char[N]; strcpy(Katinas::Vardas, Vardas); } void Katinas::Rodo() { cout << Vardas << endl; } void Katinas::operator + (char * A) { strcat(Vardas, A); } void Katinas::operator - (char C) { for(int i=0; *(Vardas+i) != '\0'; ) { if(*(Vardas + i) == C) for(int j=i; j<(strlen(Vardas)-1); j++) *(Vardas + j) = *(Vardas + j+1);
84
} }
i++;
Katinas::~Katinas() { delete Vardas; } // Pagrindin programa void main(void) { Katinas A ("Batuotas ir piktas"); A.Rodo(); A + " Rudas! "; A.Rodo(); A - 'a'; A.Rodo(); getch(); }
85
// Klas Lapas class Lapas { int x; float y; char z; public: Lapas(int, float, char ); // Konstruktorius Lapas() { x = 0; y = 0; z = 'z'; } // Konstruktorius void Rodo(char *); void Suma(int Sk) { x = x + Sk; } void Suma(float Sk) { y = y + Sk; } void Suma(char Sk) { z = Sk; } void Suma(int A, char B) { x = x + 2.0 * A; z = B + 4; } }; // Lapas metodai Lapas::Lapas(int A, float B, char C) { x = A; y = B; z = C; } void Lapas::Rodo(char *Eilute) { cout << Eilute << " : "; cout << " x= " << x << " y= " << y << " cout << endl; } // Pagrindin programa void main(void) { Lapas A; Lapas B(5, 3.4, 'C'); A.Rodo("Objektas A-1"); B.Rodo("Objektas B-1"); A.Suma(5); B.Suma('K'); A.Rodo("Objektas A-2"); B.Rodo("Objektas B-2"); A.Suma((float)35.25); B.Suma(5, 'A'); A.Rodo("Objektas A-3"); B.Rodo("Objektas B-3"); getch(); }
z= " << z;
86
p->Rodo() irinks protvio klass funkcij. Jei p = &Kn1, tai p->Rodo() irinks sukurtos klass KnygaPirma funkcij. Reikalavimai virtualioms funkcijoms: Virtuali funkcij prototipai protvio ir palikuonio klasse turi sutapti. Prieingu atveju funkcija bus laikoma perkraunama, o ne virtualia. Virtuali funkcija turi bti klass komponente (negali turti specifikatoriaus friend). Destruktorius gali turti specifikatori virtual, konstruktoriui tas draudiama. Jei funkcija paskelbta virtualia, tai i savyb ji isaugo visoms j paveldjusioms klasms. Jei kurioje nors sukurtoje klasje virtuali funkcija neaprayta (praleista), tai naudojama protvio klass versija. Jeigu praleidus virtuali funkcij, sukurtoje klasje negalima naudoti protvio klass funkcijos, tai ji paskelbiama pure virtual function. virtual Funkcijos_tipas Funkcijos_vardas (parametrai)=0; tada visose j paveldjusiose klasse turs bti sava virtualios funkcijos versija. Jei kuri nors klas turi nors vien pure funkcij, tai ji vadinama abstrakia. J galima naudoti tik kaip protvio.
6.7 pratimas. Sukuriama klas Lapas, kurioje metodas Rodo paymimas virtualiu. Turime dvi palikuonio klases KnygaPirma ir KnygaAntra. ia taip pat yra tokios funkcijos. Sukuriami objektai ir parodomas virtuali funkcij vartojimas.
#include <iostream.h> // Virtuals metodai #include <conio.h> // Klas Lapas class Lapas { public: virtual void Rodo( void ) { cout << " Klases Lapas versija \n"; } }; // Klas KnygaPirma class KnygaPirma: public Lapas { public: virtual void Rodo(void) { cout << " Klases KnygaPirma versija \n"; } };
88
// Klas KnygaAntra class KnygaAntra: public Lapas { public: virtual void Rodo(void) { cout << " Klases KnygaAntra versija \n"; } }; // Pagrindin programa void main(void) { Lapas *p, L1; KnygaPirma Kn1; KnygaAntra Kn2; p = &L1; p->Rodo(); // Vykdomas klass Lapas metodas p = &Kn1; p->Rodo(); // Vykdomas klass KnygaPirma metodas p = &Kn2; p->Rodo(); // Vykdomas klass KnygaAntra metodas getch(); }
6.8 pratimas. Modifikuotas 6.7 pratimas. Klasje KnygaAntra nra virtualaus metodo. Sukuriama nauja klas Katinas, kuri yra klass KnygaPirma palikuonis. ioje klasje yra metodas Rodo, kuris nra virtualus.
#include <iostream.h> // Virtuals metodai #include <conio.h> // Klas Lapas class Lapas { public : virtual void Rodo(void) { cout << "Klases Lapas versija \n"; } }; // Klas KnygaPirma class KnygaPirma: public Lapas { public: virtual void Rodo(void) { cout << "Klases KnygaPirma versija \n"; };
89
// Klas KnygaAntra class KnygaAntra: public Lapas { public: void Matau(void) { cout << "Klases KnygaAntra Matau \n"; } }; // Klas Katinas class Katinas: public KnygaPirma { public: void Rodo() { cout << "Klases Katinas versija \n"; } }; // Pagrindin programa void main(void) { Lapas *p, L1; KnygaPirma Kn1; KnygaAntra Kn2; Katinas Cat; p = &L1; p->Rodo(); // Vykdomas klass Lapas metodas cout << "\n"; p = &Kn1; p->Rodo(); // Vykdomas klass KnygaPirma metodas p->Lapas::Rodo(); // Vykdomas klass Lapas metodas Kn1.Rodo(); // Vykdomas klass KnygaPirma metodas Kn1.Lapas::Rodo(); // Vykdomas klass Lapas metodas cout << "\n"; p = &Kn2; p->Rodo(); // Vykdomas klass Lapas metodas Kn2.Matau(); // p->Matau(); Kreipinys negalimas cout << "\n"; p = &Cat; p->Rodo(); // Vykdomas klass Katinas metodas Cat.Rodo(); // Vykdomas klass Katinas metodas p->Lapas::Rodo(); // Vykdomas klass Lapas metodas // p->KnygaPirma::Rodo(); Kreipinys negalimas Cat.Lapas::Rodo(); // Vykdomas klass Lapas metodas Cat.KnygaPirma::Rodo(); // Klass KnygaPirma metodas getch(); }
90
Klases Lapas versija Klases KnygaAntra Matau Klases Klases Klases Klases Klases Katinas versija Katinas versija Lapas versija Lapas versija KnygaPirma versija
6.9 pratimas. Klasje Lapas apraomas metodas Rodo abstraktus. ios klass palikuonyse yra savos funkcijos, kurios atliekamos vykdymo metu.
#include <iostream.h> // Virtuals metodai #include <conio.h> // Klas Lapas class Lapas { public: virtual void Rodo(void) = 0; }; // Klas KnygaPirma class KnygaPirma: public Lapas { public: void Rodo(void) { cout << "Klass KnygaPirma versija \n"; }; // Klas KnygaAntra class KnygaAntra: public Lapas { public: void Rodo(void) { cout << "Klass KnygaAntra versija \n"; }; // Klas Katinas class Katinas:public KnygaPirma { public: void Rodo()
91
{ cout << "Klass Katinas versija \n"; }; // Pagrindin programa void main(void) { Lapas *p; KnygaPirma Kn1; KnygaAntra Kn2; Katinas Cat; p = &Kn1; p->Rodo(); p = &Kn2; p->Rodo(); p = &Cat; p->Rodo(); getch(); }
// Vykdomas klass KnygaPirma metodas //Vykdomas klass Lapas KnygaAntra metodas // Vykdomas klass Katinas metodas
92
// Klas Knyga class Knyga { public: Knyga(char *, char *, char *); // Konstruktorius void Rodo(void); // Ivedimas ekrane friend Lentyna; // Draugika klas private: char pav[64]; char autorius[64]; char leidykla[64]; }; // Knyga metodai Knyga::Knyga(char *pav, char *autorius, char *leidykla) { strcpy(Knyga::pav, pav); strcpy(Knyga::autorius, autorius); strcpy(Knyga::leidykla, leidykla); } void Knyga::Rodo(void) { cout << "Pavadinimas: " << pav << endl; cout << "Autorius: " << autorius << endl; cout << "Leidykla: " << leidykla << endl; } // Klas Lentyna class Lentyna { public: void Keisti(Knyga *, char *); char *Rasti(Knyga); }; // Lentyna metodai // Klass Knyga tipo objekte pakeiia leidyklos pavadinim void Lentyna::Keisti(Knyga * Kn, char * Leid) { strcpy(Kn->leidykla, Leid ); } // Praneamas klass Knyga tipo objekto leidyklos pavadinimas char *Lentyna::Rasti(Knyga Kn) { static char vardas[64]; strcpy(vardas, Kn.leidykla); return(vardas); } // Pagrindin programa void main(void) { Knyga K("Informatika I dalis",
93
" J.Adomavicius ir kt.", "KTU"); Lentyna L; K.Rodo(); L.Keisti(&K, "Technologija"); K.Rodo(); getch(); }
Funkcijai, nepriklausaniai jokiai klasei, gali bti suteiktas draugikos titulas. Ta funkcija gyja teis naudotis klass duomenimis ir metodais. Tose klasse, kurios t funkcij kvieia bti draugika, raomas funkcijos prototipas su atributu friend. 6.11 pratimas. Funkcija Lygu skelbiama draugika dviejose klasse: Point ir Circle. Funkcija palygina skirting klasi objekt naudojamas spalvas ir pranea ekrane, ar jos lygios, ar nelygios. Klas Vieta skirta saugoti ymeklio vietos ekrane koordinatms. Konstruktorius Vieta apibria sukurto objekto pradin pozicij. Metodas set skirtas keisti koordinatms, o getX ir getY koordinatms spausdinti ekrane. Klas Point paveldi klas Vieta ir turi duomen lauk spalvos kodui saugoti. Konstruktorius formuoja tako vietos ekrane koordinates ir spalv. Metodas PutPoint padeda tak grafiniame ekrane. Klasje skelbiama funkcija Lygu. Klas Circle paveldi klas Vieta ir turi duomen laukus apskritimo spalvos kodui bei spinduliui saugoti ir papildom darbui. Konstruktorius formuoja apskritimo centro koordinai, spindulio ir spalvos pradines reikmes. Metodas PutCircle bria apskritim grafiniame ekrane. Klasje skelbiama funkcija Lygu.
#include <iostream.h> #include <graphics.h> #include <conio.h>
94
#include <stdio.h> #include <stdlib.h> class Circle; // Klas Vieta class Vieta { protected: int x, y; public: Vieta(int Xp, int Yp) { x = Xp; y = Yp; } void set(int a, int b) { x = a; y = b; } int getX() { cout << "x= " << x << endl; return x; } int getY() { cout << "y= " << y << endl; return y; } }; // Klas Point class Point: public Vieta { int Spalva; public: Point(int Xp, int Yp, int Cp); void PutPoint() { putpixel(x, y, Spalva); } friend void Lygu(Point p, Circle c); }; // Klas Circle class Circle: public Vieta{ int Spalva, T, R; public: Circle(int Xp, int Yp, int Cp, int Rp); void PutCircle() { T = getcolor(); setcolor(Spalva); circle(x, y, R); setcolor(T); } friend void Lygu(Point p, Circle c); }; // Metodai Point::Point(int Xp, int Yp, int Cp): Vieta( Xp, Yp ) { Spalva = Cp; } Circle::Circle(int Xp, int Yp, int Cp, int Rp): Vieta(Xp, Yp) { Spalva = Cp; R = Rp; } void Lygu(Point p, Circle c) { if (p.Spalva == c.Spalva) cout << "Spalvos sutampa\n"; else cout << " Skirtingos spalvos \n"; }
95
Pagrindin programa
Point Taskas(200, 100, 3); Circle C1 (400, 200, 3, 100); Circle C2 (200, 200, 1, 50 ); Taskas.PutPoint(); C1.PutCircle(); C2.PutCircle(); Lygu(Taskas, C1); Lygu(Taskas, C2); getch(); closegraph(); // Ekrane padedamas takas // Ekrane briamas apskritimas // Ekrane briamas apskritimas
// Praneama, kad spalvos lygios // Praneama, kad spalvos skirtingos
} // Grafinio ekrano paruoimas void Grafika() { int A = DETECT, B; initgraph(&A, &B, "c:\\"); if (graphresult() != grOk) { printf("Klaida: %i %i\n ", A, B); exit(1);} }
Galima kitos klass metodus skelbti draugikais savo klasje ir leisti jiems naudotis duomen laukais. Tam reikia klasje A parayti su atributu friend klass B metod (funkcij) prototipus. 6.12 pratimas. Turime klas Kitoks, kurioje yra trys metodai, skirti darbui su kitos klass duomen laukais: keisti apskritimo vietai ekrane, spindulio reikmei ir spalvai. Klasje Circle tie metodai skelbiami draugikais. Pagrindinje funkcijoje sukuriami du klass Circle objektai C1 ir C2. Nubriamas ydras apskritimas C1 duomenimis. Sukurtas klass Kitoks objektas CC pakeiia objekto C1 visus duomenis. Nubriamas raudonas apskritimas. Pakeiiamas C1 spindulys ir nubriamas raudonas maesnio spindulio apskritimas
96
(gauname du raudonus koncentrikus apskritimus). Toliau tas pat padaroma su C2 apskritimu: du koncentriki violetiniai apskritimai.
#include #include #include #include #include <iostream.h> <graphics.h> <conio.h> <stdio.h> <stdlib.h>
class Circle; // Klas Kitoks class Kitoks { public: void Dydis(Circle *, int); void Vieta(Circle *, int, int); void Spalva(Circle *, int); }; // Klas Circle class Circle { int Spalva, T, R, x, y; public: Circle(int Xp, int Yp, int Cp, int Rp); void PutCircle() { T = getcolor(); setcolor(Spalva); circle(x, y, R); setcolor(T); } friend void Kitoks::Dydis(Circle *, int); friend void Kitoks::Vieta(Circle *, int, int); friend void Kitoks::Spalva(Circle *, int); }; // Metodai Circle::Circle(int Xp, int Yp, int Cp, int Rp) { Spalva = Cp; R = Rp; x = Xp; y = Yp; } void Kitoks::Dydis(Circle *CC, int D) { CC->R = D; } void Kitoks::Vieta(Circle *CC, int Nx, int Ny) { CC->x = Nx; CC->y = Ny; } void Kitoks::Spalva(Circle *CC, int S) { CC->Spalva = S; }; // Pagrindin programa void Grafika();
97
Circle C1(400, 200, 3, 100); Circle C2(300, 300, 5, 50 ); C1.PutCircle(); Kitoks CC;
CC.Vieta(&C1, 100, 100);// Keiiama C1 vieta CC.Spalva(&C1, RED); // Keiiama C1 spalva C1.PutCircle(); // Briamas C1 raudonas apskritimas CC.Dydis(&C1, 25); C1.PutCircle(); C2.PutCircle(); CC.Dydis(&CC, 30); C2.PutCircle(); getch(); closegraph(); // Keiiamas C1 spindulys // Briamas C1 raudonas apskritimas // Briamas C2 violetinis apskritimas // Keiiamas spindulys // Briamas C2 violetinis apskritimas
} // Grafinio ekrano paruoimas void Grafika() { int A = DETECT, B; initgraph(&A, &B, "c:\\"); if(graphresult() != grOk) { printf("Klaida: %i %i\n ", A, B); exit(1); } }
98
Dinamikai skirtos atminties atsisakoma operatoriumi delete arba funkcija free: delete <rodykl>; void free (<rodykl>); 6.13 pratimas. Turime klas Lapas. Sukuriama rodykl p klas Lapas. Sukuriamas dinaminis objektas, kurio adresas saugomas rodyklje p.
#include <iostream.h> #include <string.h> #include <stdlib.h> // Klas Lapas class Lapas { char *L; public : Lapas(char * T) { L = T;} void Kitas(char *K) { strcpy(L, K); } void Rodo(void) { if (L) cout << L << " lapas \n"; else cout << "Neturiu lapo\n"; } }; // Pagrindin programa void main(void) { Lapas *p; p = new Lapas("Klevo "); if (!p) { cout << "Truksta atminties\n"; exit( 0 ); } p->Rodo(); p->Kitas("Liepa"); p->Rodo(); free(p); }
6.14 pratimas. Turime klas Lapas. Sukuriame rodykli masyv p, kuriame saugosime rodykles tris objektus klass tipo Lapas. Suformuojamas rodykli objektus masyvas. Objektai saugo tuias eilutes. Po to klaviatra suvedami odiai ir patalpinami objekt duomen laukus L. Programos pabaigoje atspausdinami objekt saugomi odiai ir objektai paalinami i atminties.
99
#include <iostream.h> #include <string.h> #include <stdlib.h> // Klas Lapas class Lapas { char *L; public : Lapas() { L = NULL; } void Kitas(char *K) { L = new char[10]; strcpy(L, K); } void Rodo(void) { if (L) cout << L << " lapas \n"; else cout << "Neturiu lapo\n"; } }; // Pagrindin programa void main(void) { int i; Lapas *p[3]; char T[10]; for (i=0; i<3; i++) { // p[i] = new Lapas(); cout << "*** "; p[i]->Rodo(); } cout << "--------------\n"; for (i=0; i<3; i++) { // cout << "Iveskite: "; cin >> T; cout << endl; p[i]->Kitas(T); } // for (i=0; i<3; i++) p[i]->Rodo(); for (i=0; i<3; i++) free(p[i]); // cout << "Pabaiga\n"; }
Masyvo sudarymas
Duomen vedimas
Spausdinimas Naikinimas
100
6.15 pratimas. Turime klas kurio elementais yra objekt upildymas seka, nes sraas sunaikinimas.
Lapas. Sukuriamas tiesinis dinaminis sraas, klass Lapas tipo objektai. Parodomas srao duomenimis, srao spausdinimas (atvirktin buvo formuojamas pradi) ir srao
#include <iostream.h> #include <string.h> #include <stdlib.h> // Klas Lapas class Lapas { char *L; public : Lapas() { L = NULL; } void Kitas(char *K) { L = new char[10]; strcpy(L, K); } void Rodo(void) { if (L) cout << L << " lapas \n"; else cout << "Neturiu lapo\n"; } ~Lapas() { free(L); } }; // Sraas struct sar { Lapas *Medis; sar *sek;}; // Pagrindin programa sar *Naikinti(sar *); void main(void) { sar *P = NULL, *R; int i; char T[10]; for(i=1; i<=3; i++) { cout << "\nIveskite: "; cin>>T; R = new sar; R->sek = P; P = R; P->Medis = new Lapas();
// Srao formavimas
101
} // Srao naikinimas sar *Naikinti(sar *P) { sar *R; while(P) { R = P; P = P->sek; R->Medis->~Lapas(); free( R ); } return P; }
P->Medis->Kitas(T); } R = P; // Srao spausdinimas while(R) { R->Medis->Rodo(); R = R->sek; } P = Naikinti(P); // Srao naikinimas if (!P) cout << "Sarasas tuscias\n"; cout << "Pabaiga\n";
102
Dinamins struktros elemento reikm gali bti bet kokia statin arba dinamin struktra, o ryio dalyje yra raomos rodykls kitus to paties srao elementus. Kadangi dinamini struktr element reikms ir nuorodos yra apraomos skirting tip duomenimis, sra elementai realizuojami raais, kuri struktra yra tokia:
struct <Vardas> { <Reikms laukai>; <Ryio dalies laukai>; }
Homogeninse dinaminse struktrose, kuriose visi elementai yra vienodo tipo, apraant ryio dalies rodykles, reikia nurodyti paios naujai apraomos struktros tip. Kreipimasis struktros aprayme save pai vadinamas rekursija, o struktros, kuriose yra tokie kreipiniai, vadinamos rekursyviomis. Rekursyvios struktros elemento aprao pavyzdys:
struct list {int data; struct list *next; } // Rekursyvi nuoroda
I element, kuri ryio dalyje yra viena rodykl, galima sudaryti tik tiesiniais sraais vadinamas nuosekliai sujungtas grandines. Papildant 103
tiesinius sraus tvarkymo procedromis ir iorinio ryio priemonmis, galima sudaryti tokias tipines j modifikacijas: tiesinius sraus, kuriuose nra apribojim srao element analizei, tvarkymui ir apdorojimui; eiles, kuriose nauji elementai prijungiami srao gale, o skaitymui yra prieinamas tik pirmasis elementas (struktra FIFO first in, first out); stekus, kuriuose galima skaityti tik vliausiai rayt element (struktra LIFO last in, first out); dekus, kuriuose elementai gali bti raomi ir skaitomi tiek srao pradioje, tiek gale (struktra DEQUE double-ended que). Tokios srains struktros labai plaiai vartojamos ne tik taikomosiose, bet ir sisteminse programose. Pavyzdiui, stekuose yra saugomi pertraukiam proces parametrai, organizuojami lokals duomenys. Eils yra populiari pagalbini (buferini) atmini, kuriose kaupiami i lt rengini gaunami arba juos siuniami duomenys, organizavimo priemon. Kiekvieno srao tipo realizavimui skirta dinamin struktra turi turti jos elementus apraani struktr, rodykl arba rodykles ioriniams ryiams skirtus elementus ir tokias tvarkymo operacijas: naujo srao sukrimo; naujo elemento prijungimo; elemento paalinimo; srao skaitymo ir analizs; srao tvarkymo. Sudarant sra tvarkymo procedras, reikalingas papildomas susitarimas apie tai, kaip bus ymima srao pabaiga ir kaip bus ymimas tuias sraas. Yra priimta tiek srao pabaig, tiek tui sra dinaminse struktrose ymti nuline rodykle NULL.
104
Srao elemento struktra gali bti apraoma vienu arba dviem sakiniais:
struct List { int Sk; struct List *next; } *First; struct List { int Sk; struct List *next; }; List *First;
Tiesinio srao grafinis vaizdas parodytas 7.1 pav. ia elementas vaizduojamas staiakampiu, padalintu tiek dali, kiek yra lauk jo struktroje: duomen ir adreso. Duomen lauke saugomos reikms. Rodykls tipo lauke saugomas adresas (nuoroda) kit srao element. Nuoroda pavaizduota linija su rodykle gale. Tos linijos pradia yra nuorodos lauko viduje. Rodykl remiasi element vaizduojant staiakamp bet kurioje vietoje. Jeigu nuoroda neegzistuoja, tuomet linija nra briama ir laukas lieka tuias. Tai reikia, kad adreso reikm neapibrta, iukl. Realiose programose tokia situacija yra neleistina. P 16 58 8 4
NULL 7.1 pav. Tiesinio srao vaizdavimas
16
58
P
NULL
<16>
<58>
<8>
<4>
NULL
Btina urayti tuio adreso reikm konstant NULL. Grafikai tai gali bti ymima vairiais sutartiniais enklais, kas leidia konstruoti maesns apimties paveiksllius. enklai naudojami, kuomet nagrinjamos struktros nra siejamos su konkreia realizacija (pvz., Turbo Paskalyje tuio adreso konstanta yra Nil). Sraus suvokti lengviau, kai sutartini paymjim maiau, todl bus raoma NULL. Be to paveikslliuose bus 105
naudojamas tokio tipo paymjimas: reikm. Tai reik adres viet, kur saugoma nurodyta reikm, pavyzdiui skaiiaus 16 nuoroda bus urayta 16 (7.2 pav.). Tiesinio srao formavimo ir periros iliustracijai panaudosime program Srao formavimas. ioje programoje yra sukurta klas DinSar, kurioje yra apraytas kintamasis P (dinaminio srao pradios rodykl), konstruktorius, destruktorius, bei metodai sraui formuoti ir spausdinti. Programa naudoja klass DinSar objekt A. Klasje yra keturi metodai: srao formavimui paraytas metodas Formuoti. Jame yra imami sveiki skaiiai i failo ir perduodami metodui Elementas, kuris sukuria dinamin element su ta reikme ir prijungia prie formuojamo srao pradios (steko formavimo bdas); srao perirai yra metodas Spausdinti, kuris srao element reikmes surao ekrane viena eilute; klass DinSar konstruktorius DinSar sukuria objekt A ir io objekto srao pradios rodyklei P suteikia reikm NULL, o destruktorius ~DinSar naikina i atminties sra ir objekt A. Detaliau veiksmai bus aptariami atskiruose skyreliuose.
// Srao formavimas (stekas) #include <iostream.h> #include <stdio.h> #include <stdlib.h> // Struktros tipo apibrimas typedef struct list {int sk; struct list *next; } sar; // Klass Dinsar apibrimas class DinSar { sar *P; // Srao pradia public: DinSar(); // Konstruktorius ~DinSar(); // Destruktorius void Formuoti(FILE *); // Formuoja sra void Elementas(int); // Prijungia element void Spausdinti(); // Spausdina sra }; // Pagrindin programa void main() { FILE *D;
106
} // Konstruktorius DinSar::DinSar() { P = NULL; } // Destruktorius DinSar::~DinSar() { sar *D = P; while(P != NULL) { D = P; // Rodykl naikinam element P = P->next; // Kito elemento adresas delete( D ); } // Elemento naikinimas } // Formuoja netiesiogin sra void DinSar::Formuoti(FILE *F) { int k; while(!feof(F)) { fscanf(F, "%i", &k); Elementas(k); } } // Prijungia element void DinSar::Elementas(int Sk) { sar *R; R = new sar; // Naujo elemento sukrimas R->sk = Sk; // Upildymas duomenimis R->next = P; // Prijungimas prie srao P = R; // Srao pradios pakeitimas } // Srao spausdinimas void DinSar::Spausdinti() { sar *D = P; while(D != NULL) { cout << D->sk << " "; // Reikms spausdinimas D = D->next; } // Kito elemento adresas cout << "\n"; }
DinSar A; // Klass DinSar objektas D = fopen("Duom.dat", "r"); if (D == NULL) { cout << "Failo 'Duom.dat' nepavyko atidaryti"; exit (1); } A.Formuoti(D); // Formuojamas sraas fclose(D); A.Spausdinti(); // Spausdinamas sraas A.~DinSar();
107
NULL
Metodo Formuoti darbo pradioje formuojamas sraas yra tuias, t.y. srao pradios rodykls P reikm yra lygi NULL. Jeigu failas bus tuias, tai tokia reikm ir pasiliks metodui baigus darb. i situacija grafikai parodyta 7.3 pav. Tarkime duomen faile Duom.dat yra tokia skaii seka: 18 7 4 9 Pirm kart cikle (7.4 pav.) i failo perskaitoma reikm k = 18. Ji perduodama metodui Elementas. is metodas sukuria nauj element 108
su ta reikme (1 ir 2 sakiniai), po to jis prijungiamas prie srao pradios (3 sakinys), o srao pradios rodykl perkeliama prie to naujo elemento (4 sakinys), t.y. naujasis elementas tampa pirmuoju srae. Antr kart cikle gauta nauja reikm k = 7 tampa nauju elementu jau turimo srao pradioje (7.5 pav.).
P NULL 18 R NULL Sukuriamas elementas, kurio adres saugo rodykl R, ir upildomi laukai: 1, 2, ir 3 veiksmai.
P 18 R NULL
Srao pradia yra naujas elementas, kurio adres saugo rodykl R. Tas adresas yra uraomas rodyklje P. 4 veiksmas.
Perklus visus faile esanius skaiius sra, srao pradios rodykl P rodys srao pradi (saugo pirmojo srao elemento adres). Suformuoto srao vaizdas parodytas 7.6 pav. Matome, kad suformuotas sraas saugo failo duomenis prieinga tvarka, nes nauji elementai buvo prijungiami prie srao pradios. Pastaba. Veiksmuose su srao elementais srao pradios rodykls P prarasti negalima, nes tuomet srae esantys duomenys bus neprieinami (prarasti).
P 18 NULL 7 R Pirmuoju veiksmu sukuriamas naujas elementas, kurio adres saugo rodykl R. Antruoju veiksmu raoma reikm k = 7
109
18 NULL 7 Ketvirtuoju veiksmu srao pradios rodykl perkeliama prie naujo elemento. 7.5 pav. Antrasis elementas srae
9
P
18
NULL
110
Kiekviena programa privalo tvarkyti naudojam atmint taip, kad nesusidaryt avarini situacij. Vienas i toki veiksm yra nebereikalingo srao paalinimas i atminties. Pavyzdio programoje tam skirta destruktorius ~DinSar. ia naudojama papildoma rodykl D alinamo elemento adresui atsiminti. Sraas nuosekliai peririmas naudojant jo rodykl P, kuri turi srao pradios adres. Pirmu veiksmu atsimenamas srao pradios elemento adresas (D = P). Po to srao pradia yra perkeliama prie tolimesnio elemento (P = P->next;), o atjungtas nuo srao elementas D yra paalinamas. 9
P D Darbinei rodyklei D suteikiamas srao pradios adresas: D = P; Galima atspausdinti pirmojo elemento reikm 9: cout << D->sk;
18
NULL
9
P D
18
NULL
Pereinama prie kito elemento: D = D->next; Galima atspausdinti elemento reikm 4: cout << D->sk;
111
9
P D
18
NULL
Pereinama prie kito elemento: D = D->next; Galima atspausdinti elemento reikm 7: cout << D->sk;
9
P D
18
NULL
Pereinama prie kito elemento: D = D->next; Galima atspausdinti elemento reikm 18: cout << D->sk;
9
P D NULL
18
NULL
Pereinama prie kito elemento: D = D->next; Papildoma rodykl D gauna paskutinio elemento ryio dalyje esant adres NULL. i reikm yra srao periros pabaigos poymis. 7.7 pav. Srao element perira
7.9 pav. Patikrinkite, ar tikrai tinka tie patys veiksmai, kai R rodo paskutin srao element (papildymas gale). 9
P R 123
18
NULL
NULL
Rodykl R rodo element, po kurio reikia terpti nauj element S. Jis suformuotas pilnai. Adresin dalis turi tuio adreso reikm.
9
P R
18
NULL
123
Naujam elementui S suteikiamas adresas elemento, esanio toliau u elemento R: S->next = R->next; Elemento R rodykl nukreipiama nauj element: R->next = S;
Atskiras srao papildymo atvejis yra tada, kai sraas visikai tuias. Tuomet pakanka srao pradios rodyklei P suteikti naujo elemento adres:
P = S;
terpimas prie nurodyt element tokio tipo srauose negalimas, nes elementai susieti rodyklmis viena kryptimi. Galimas tik vienas atvejis: terpti prie pirmj, kas tolygu sra papildyti pradioje nauju elementu. terpim prie galima pakeisti terpimu po. Tam reikia surasti element, esant prie mus dominant. Pavyzdiui, reikia rasti element, esant prie didiausi:
sar *R, *R1, *T,*T1; // Didiausios reikms elementas // Elementas, esantis prie didiausi // Papildomos rodykls srao perirai
113
Perirjus sra iki galo, rodykl R rodys element su didiausia reikme, o rodykl R1 rodys element, esant prie didiausi. Jos abi rodys pirmj element, kai jo reikm srae didiausia. Tai reikia, kad veiksmuose su sraais visuomet btina nagrinti visas galimas situacijas. terpim prie galima atlikti paprasiau. Jeigu darbo su srau veiksmai neprietarauja element reikmi keitimui vietomis, tuomet siloma tokia terpimo veiksm seka: Turime surast element R, prie kur reikia terpti element S; Sukeiiamos R ir S element reikms (ia int k;):
k = R->sk; R->sk = S->sk; S->sk = k;
// alinamas elementas R
// yra pirmas, t.y. R = P
Klasikinis elemento paalinimo i srao atvejis yra, kai turime rodykl alinamj element R ir prie j esant R1 (7.10 pav.).
114
9
P
18
NULL
R1
R1->next = R->next
alinant elementus btina atlaisvinti atmint, kuri jie uima. Tam naudojama funkcija delete. Labiau patyrs programuotojas ras lakonikesni priemoni veiksmams atlikti. Kaip vien i j galima pasilyti nurodyto elemento R alinimui (7.11 pav.): *R = *R->next; ia yra tolesnio elemento turinio perklimas nurodyt. Tuo sunaikinamas elemento R turinys. Srae turime du vienodus elementus, kuri tik vienas prieinamas. 9
D
18
NULL
R
7.11 pav. Sraas po elemento paalinimo
Pastaba: io veiksmo negalima taikyti, kai R rodo paskutin element, nes toliau jau nra kit element. iam atvejui btina atskira veiksm grup. Tokios element alinimo procedros negalima taikyti sudtingose srainse struktrose, kur duomen element adresai naudojami naujoms sra sekoms formuoti. Duomenys negali keisti savo pradinio adreso.
struktra elemente nesudtinga arba duomen apimtis nedidel. Visais atvejais tikslinga elemente turti tik vien lauk duomenims nurodyti, o duomen pilna hierarchin struktra yra apraoma atskirai.
void DinSar::Rikiavimas() { sar *R = P, *R1; int k; while(R != NULL) { R1 = R->next; while(R1 != NULL) { if (R1->sk > R->sk) { k = R->sk; R->sk = R1->sk; R1->sk = k; } R1 = R1->next; } R = R->next; } }
// Reikmi sukeitimas
Programose dana situacija, kai negalima keisti srao element duomenis vietomis. Reikia sukeisti element sek srae, t.y. keiiamos nuorodos. Norint sukeisti vietomis srae du elementus, reikia turti rodykles ne tik keiiamus elementus, bet ir prie juos esanius. ia reiks pasirpinti keiiam element kaimynais prie ir po (7.12 pav.).
NULL
R1
S1
Reikia sukeisti vietomis elementus R ir S. Tai galima padaryti taip: R1 elemento kaimynu po padaromas elementas S:
R1->next = S; S1->next = R;
116
pap = R->next;
Reikia neumirti patikrinti keiiam element srae padt (pradioje, gale ar viduje). Gaunama sudtinga veiksm seka. ymiai paprasiau suformuoti nauj tvarking sra. Tai galima padaryti taip: rasti duotame srae element pagal duot rikiavimo rakt; paalinti surast element i duoto srao; prijungti element prie naujo srao.
void DinSar::Surikiavimas() { sar *T = P, // T senojo srao pradia *D, *D1, *S, *S1; P = NULL; // Naujas sraas tuias while(T) { D = T; // Gretimi du elementai srao perirai D1 = T; S = T; S1 = T; // Didiausias S ir prie j esantis S1 while(D) { // Didiausio paieka if (D->sk > S->sk) { S = D; S1 = D1; } D1 = D; D = D->next; } if (S == T) T = T->next; // alinimas else S1->next = S->next; S->next = P; // Prijungimas prie naujo srao P = S; } // Naujo srao pradia }
iame pavyzdyje nebuvo kuriami nauji elementai. Jie buvo sujungiami nauja seka, pasinaudojant jau inomomis operacijomis. T pat rezultat galima gauti trumpesniu keliu. Silome panagrinti burbuliuko bdu rikiuojani funkcij:
void DinSar::RikiavBurb() { sar **T, *R;
117
int flag = 1; while(flag) { flag = 0; T = &P; while((*T)->next) { if ((*T)->sk > (*T)->next->sk) { R = *T; *T = R->next; R->next = (*T)->next; (*T)->next = R; flag = 1; } T = &(*T)->next; } } }
Tvarkymo ir kiti veiksmai vienkrypiuose srauose yra sudtingi, reikalauja papildom darbo snaud. ymiai yra patogesni dvikrypiai sraai, kur kiekvienas elementas turi rodykles kaimynus prie ir po. Realiai vienkrypiai sraai naudojami su tam tikrais apribojimais: stekai, eils, dekai.
Papildykite klas tam skirtais metodais FormuotiEile ir ElementasEile ir juos ibandykite. Atkreipkite dmes naujo elemento sukrimo ir prijungimo skirtumus steko ir eils atvejais. 18
P 9
NULL
118
7.13 pav. Eils formavimas void DinSar::FormuotiEile(FILE *F) { sar *D = NULL, *G; int k; if (!feof(F)) { fscanf(F, "%i", &k); ElementasEile(&D, k); G = D; } while(!feof(F)) { fscanf(F, "%i", &k); ElementasEile(&G, k); } P = D; } void DinSar::ElementasEile(sar **P, int Sk) { sar *R; R = new sar; // Suformuojamas elementas R->sk = Sk; R->next = NULL; if (*P) { // Elementas prijungiamas gale (*P)->next = R; *P = R; } else *P = R; // arba jis dar pirmas srae } // Pirmas elementas // Galo rodykl G // Kiti elementai // Prijungimas gale
// Veiksmai su steku #include <iostream.h> struct stack { char d; // Steko element tipas struct stack *next; }; class stekas { public: stack *first; // Steko realizacija (rodykl) stekas() { first = NULL; } // Konstruktorius ~stekas() {}; // Destruktorius stack *Get(){ return first; }; void Push(char); // Steko papildymas void Ekranas(stack *); // Steko perira char Pop(); // Steko skaitymas }; // Pagrindin programa main() { // Tvarkoma eilut ir rodykl j char prad[] = "abcdefgh", *p = prad; stack *d; // Darbinis kintamasis stekas A; // Objekto apraymas // Tvarkomos eiluts parodymas ekrane cout << "Siunciami i steka simboliai: " << p << endl; while(*p) A.Push(*p++); // Steko upildymas cout << "Steko patikrinimas:\n"; d = A.Get(); A.Ekranas(d); // Steko turinio "abcdefgh" parodymas cout << "Skaitymas po viena simboli:\n"; while(A.first) cout << A.Pop() << endl; // Steko ivalymo kontrol if (!A.first) cout << "Tuscias stekas\n"; A.~stekas();
} // Steko papildymas void stekas::Push(char e) { stack *newe; newe = new stack; // Atminties skyrimas newe->d = e; // Naujo elemento reikm newe->next = first; // Senos steko dalies prijungimas first = newe; // Naujas steko adresas }
120
// Rekursyvus steko turinio patikrinimas void stekas::Ekranas(stack *p) { if (p) { // Rekursijos nutraukimo slyga cout << p->d; // Reikms parodymas ekrane Ekranas(p->next);} // Rekursyvus kreipinys tolimesn element else cout << endl; } // Elemento paalinimas i steko char stekas::Pop() { stack *old= first; // Adreso isaugojimas char e = first->d; // Reikms isaugojimas first = first->next; // Pirmo elemento alinimas delete old; // Atminties ilaisvinimas return e; // Paalinto elemento reikm }
Rekursyvi funkcija Ekranas stekui yra perteklin. Joje demonstruojama, kad rekursyvaus tipo dinamini struktr element perrinkim galima glaustai aprayti rekursyviomis funkcijomis. Element perrinkimui taip pat galima vartoti funkcijos viduje deklaruojam lokali rodykl. Naudojant i perrinkimo technik, galima sudaryti toki funkcijos Ekranas modifikacij:
// Ciklinis steko turinio patikrinimas void stekas::Ekranas1(stack *p) { stack *temp = p; // Pagalbin rodykl while(temp) { // Steko perrinkimo ciklas cout << temp->d; // Reikms parodymas ekrane temp = temp->next; } // Rodykl tolimesn element cout << endl; }
Standartins stek valdymo procedros yra labai paprastos ir greitos, todl stekai plaiai vartojami laikinam duomen saugojimui tiek taikomosiose, tiek sisteminse programose. Stekus galima realizuoti ne tik dinaminmis struktromis, bet ir kitais bdais: aparatros pagalba, vartojant specialius indeks registrus. Pavyzdiui, steko registro pagalba tvarkom steko atminties segment kompiliatoriai vartoja lokaliems kintamiesiems saugoti ir informaciniams ryiams tarp funkcij palaikyti.
121
Norint i eils padaryti dek, reikia sudaryti dar vien funkcij, kuri leist paalinti galinius eils elementus. Rekursyvios galinio eils elemento alinimo funkcijos realizacija anksiau apraytai struktrai stack atrodo taip:
// alinimas i eils galo char stekas::Del(stack *p) { char s; // alinamasis elementas if (!p->next) { // Jeigu eilje tik 1 elementas s = p->d; delete first; // alinamas pirmas eils elementas first = NULL; return s; }
122
else if (!p->next->next){ s = p->next->d; delete p->next; // alinamas priepaskutinis eils elementas p->next = NULL; return s; } // Jeigu eilje daugiau kaip vienas elementas else Del(p->next); // Rekursija }
Eils pabaigos paiekai vartoti rekursyv arba ciklin jos element perrinkim yra neracionalu. Toks paiekos bdas gali bti pateisinamas tiktai trumpose eilse. Tvarkant ilgas eiles, j element perrinkimo patartina vengti. Tai galima padaryti, papildant eil galinio elemento rodykle, kurios reikm bt vartojama ir keiiama papildant eil naujais elementais. Tokios eils ioriniam apraymui rekomenduojama sudaryti dviej rodykli struktr ir j tvarkyti modifikuotomis steko tvarkymo funkcijomis. Kaip tai yra daroma, iliustruojama 7.3 pratime.
first NULL last
7.3 pratimas. Naudodami pateiktus simboli eils su dviem rodyklm struktros ir jos tvarkymo funkcij apraymus, sudarykite demonstracin program, kuri leist patikrinti i funkcij darb.
struct stack { char d; struct stack *next; }; // Srao element tipas
struct list { // Eil su dviem iorinm rodyklm stack *first; // Pradios rodykl stack *last; }; // Pabaigos rodykl // Klass apraas class eile { public: list que; // Eils realizacija eile() { // Konstruktorius que.first = NULL; que.last = NULL; };
123
}; // Eils tvarkymo funkcij realizacijos // Eils papildymas gale int eile::Add(char e) { // F-jos Add reikm 1 pranea, kad papildyta skmingai stack *newe; if (!(newe = new stack)) return 0; // Ar yra atmintyje vietos? newe->d = e; // Naujas elementas newe->next = NULL; // Pabaigos ym if (!que.last) { // Jeigu eil tuia que.last = newe; // Naujas pabaigos adresas que.first = newe; } // Naujas pradios adresas else { (que.last)->next = newe; // Elemento prijungimas que.last = newe; } // Naujas pabaigos adresas return 1; // Praneimas apie skming pabaig } // Pradinio elemento paalinimas char eile::Pop() { stack *old= que.first; // Adreso isaugojimas if (!(que.first)) return '\0'; // Praneimas apie tui eilut char e = (que.first)->d; // Reikms isaugojimas que.first = (que.first)->next; // Pirmo elemento naikinimas delete old; // Atminties ilaisvinimas return e; // Reikms grinimas }
124
Toliau veiksmuose patogu turti tik vien iorin rodykl last, kuri saugos iedo galinio (paskutinio) elemento adres, o galinio elemento ryio dalyje bus saugoma iedo pradinio elemento adresas (7.16 pav.). T rodykl (last) toliau vadinsime iedo rodykle. Pavadinimai pradia ir galas iede yra slyginiai. Naudojant iuos pavadinimus bus lengviau aikintis veiksmus iede.
Pradia 1 2 3 4 5 Pabaiga last
iediniai sraai gali bti vartojami eili realizavimui sprendiant tokius udavinius, kuriuose reikalinga daugel kart perirti t pat duomen rinkin. iedams yra taikomos tokios standartins tvarkymo operacijos: tuio iedo inicializavimas; naujo elemento terpimas iedo pradioje; papildymas nauju elementu pabaigoje; pradinio elemento paalinimas; paieka ir paalinimas. Tuio iedo, kuris ymimas tuia rodykle NULL, inicializavimas yra toks pats, kaip ir tiesinio srao. Tuo tarpu, kit operacij realizavimas skiriasi. Pavyzdiui, papildant sra naujais elementais, tenka taikyti skirtingas procedras tuiam ir netuiam sraui. Paiekos ir paalinimo operacijos metu turi bti randamas ir paalinamas elementas su duotja rakto reikme. Paieka yra vykdoma nuosekliai perrenkant iedo elementus. alinant reikia iskirti du atvejus: kai yra alinamas elementas, kur rodo iedo iorin rodykl, yra paskutinis ir kai alinamas elementas iede yra vienintelis. Pirmuoju atveju, turi bti keiiama iedo iorins rodykls reikm, o antruoju atveju, iedo iorins rodykls reikmei priskiriama NULL. Atvej, kai reikia alinti iede esant element, kur nerodo iedo rodykl, visuomet galima pakeisti atvej, kai alinamas elementas, kurio adres saugo iedo rodykl.
125
7.4 pratimas. Sudarykite demonstracin program, kuri leist patikrinti veiksmus su iediniu srau. Naudokite klas ziedas ir jos tvarkymo metod apraymus.
struct stack { char d; // iedo element tipas struct stack *next; }; class ziedas { public: stack *last; // iedo realizacija (rodykl) ziedas() { last = NULL; };// Konstruktorius ~ziedas() {}; // Destruktorius int Ins(char); // terpimas iedo pradioje int Add(char); // terpimas iedo pabaigoje char Del(); // Pirmojo iedo elemento paalinimas char DelSerch(char); // Elemento paieka ir alinimas }; // iedo tvarkymo funkcij realizavimas // terpimas iedo pradioje // Funkcijos reikm 1 pranea, kad papildyta skmingai int ziedas::Ins(char e) { stack *newe; if (!(newe = new stack)) // Ar yra atmintyje vietos? return 0; newe->d = e; // Naujas duomen elementas if (last) { // Netuio iedo slyga newe->next = last->next; // Pirmo elem. adreso perdavimas last->next = newe; } // Naujas pradios adresas else last = newe->next = newe; // Tuio iedo papildymas return 1; // Praneimas apie skming pabaig } // iedo papildymas pabaigoje // Funkcijos reikm 1 pranea, kad papildyta skmingai int ziedas::Add( char e ){ stack *newe; if (!(newe = new stack)) // Ar yra atmintyje vietos? return 0; newe->d = e; // Naujas duomen elementas if (last) { // Netuio iedo slyga newe->next = last->next; // Pirmo elem. adreso perdavimas last->next = newe; // Elemento prijungimas pabaigoje last = newe; } // Naujas pradios adresas
126
} // Pirmojo iedo elemento paalinimas char ziedas::Del() { stack *temp; // Pagalbiniai kintamieji char e; if (!last) return '\0'; // Pranesimas apie klaida if (last == last->next) { // iede vienas elementas temp = last; // alinamo elemento adresas last = NULL; // Nauja iorin rodykl } else { // iede yra keletas element temp = last->next; // alinamo elemento adresas last->next = last->next->next; // alinimas i iedo } e = temp->d; // Reikms isaugojimas delete temp; // Atminties ilaisvinimas return e; // Reikms grinimas } // Elemento paieka ir paalinimas iede char ziedas::DelSerch(char e) { stack *temp = last; // Pagalbiniai kintamieji stack *t; if (!last) return '\0'; // Praneimas apie klaid while (((temp->next) != last) && ((temp->next->d) !=e)) temp = temp->next; // Element perrinkimas if ((temp->next->d) == e) // Jeigu elem. buvo surastas if (last == temp->next) // Galinis elementas if (temp == last) { // Jeigu iede 1 elementas last = NULL; // Tuias iedas delete temp; } else { temp->next = last->next; // Elemento alinimas delete last; last = temp; } // Nauja iorin rodykl else { t = temp->next; temp->next = temp->next->next; delete t; } }
else last = newe->next = newe; // Tuio iedo papildymas return 1; // Praneimas apie skming pabaig
127
128
};
Sra su neapibrto tipo duomen element rodyklmis tvarkymo veiksmus reikia atskirti nuo elementams skiriamos atminties tvarkymo veiksm ir veiksm, kurie susij su srae saugom duomen analize, j interpretavimu. Dauguma sra tvarkymo veiksm yra universals ir juos galima aprayti tokiomis funkcijomis, kurios tinka visiems rodykli nurodomiems element tipams. Tuo tarpu, su duomen interpretavimu susij veiksmai paprastai bna specializuoti, priklauso nuo srao element reikmi tipo. Rodykli sra formavimas yra labai galinga duomen tvarkymo priemon, kuri vairioms duomen saugojimo struktroms gali suteikti naujas savybes. Pavyzdiui, tokio srao sudarymas gali pakeisti masyvo rikiavim. Be to, vienam masyvui galima sudaryti kelis rodykli sraus, apraanius skirtingus jame saugom duomen perrinkimo bdus. Apraant neapibrto tipo rodykli sra nurodom duomen interpretavimo operacijas, reikia apibrti i rodykli interpretavimo bd. Interpretavimo bdas yra apibriamas tokia struktra:
(<Duomen tipas>*) <Elemento duomen rodykl>
7.5 pratimas. Patikrinkite pratime pateiktos programos darb. Isiaikinkite joje aprayto universalaus steko tvarkymo funkcij sudarymo principus ir i funkcij pritaikym simboli stekui tvarkyti. Programoje taip pat iliustruojamas specializuotos funkcijos Show sudarymas, kuri gali perduoti ekran tik simboli steko reikmes.
// Saraas su neapibrto tipo elementais #include <iostream.h> char line[] = "Sula"; // Simboli masyvas
129
struct stack { // Universalus srao elementas void *d; // Duomen rodykl struct stack *next; }; class stekas { public: stack *first; // Steko realizacija (rodykl) stekas() { first = NULL; };// Konstruktorius ~stekas() {}; // Destruktorius stack *Get() { return first; }; // Universalios tvarkymo funkcijos void Push(void *); // Papildymas void *Pop(); // Elemento paalinimas void Show(stack *); // Steko turinio parodymas ekrane }; // Pagrindin programa main() { stack *d; // Papildomas kintamasis stekas A; // Objekto apraymas int i = 0; // Simboli masyvo indeksas cout << "Tvarkoma eilute: " << line << endl; while(line[i]) // Simboli masyvo pabaigos slyga A.Push(&line[i++]); // Rodykli steko sudarymas cout << "Steko rodomi simboliai: " << endl; d = A.Get(); A.Show(d); cout << "Trinama rodykle i pirma simboli "; cout << *((char *) A.Pop()) << endl; cout << "Liko rodykles i siuos simbolius: \n"; d = A.Get(); A.Show( d ); } // Srao tvarkymo funkcij realizacijos // Steko papildymas void stekas::Push(void *e) { stack *newe; // Papildomas kintamasis newe = new stack; // Atminties skyrimas newe->d = e; // Naujo elemento duomen rodykl newe->next = first; // Senos steko dalies prijungimas first = newe; // Naujas steko adresas }
130
// Elemento paalinimas i steko void * stekas::Pop() { stack *old = first; // Adreso isaugojimas void *e = first->d; // Reikms rodykls isaugojimas first = first->next; // Pirmo elemento alinimas delete old; // Atminties ilaisvinimas return e; } // Rekursyvus steko patikrinimas void stekas::Show(stack *p) { if (p) { // Interpretavimas ir ivedimas ekran cout << *(char *)(p->d); Show(p->next); } // Rekursyvus kreipinys tolimesn element else cout << endl; }
131
typedef struct studentas { char pavard[10]; char vardas[10]; float vidur; }; typedef struct sar { studentas *st; studentas *vd; sar *next; }; // Klass apraymas class student { sar *P; // Srao pradia public: student() { P = NULL; }; // Konstruktorius void Formuoti(FILE *); // Formuoja sra void Spausdinti(int); // Spausdina sra void Tvarkymas(int); // Tvarkymas }; // Pagrindin programa void main() { FILE *D; student A; clrscr(); D = fopen("stud.dat", "r"); if (D == NULL) { cout << "Failo Stud.dat nepavyko atidaryti!"; exit (1); } A.Formuoti(D); fclose(D); A.Spausdinti(0); // Nesurikiuotas A.Tvarkymas(1); // Rikiavimas pagal abcl A.Tvarkymas(0); // Rikiavimas pagal vidurk A.Spausdinti(1); // Abclinis sraas A.Spausdinti(0); // Sraas pagal vidurk } // Student srao formavimas void student::Formuoti(FILE *F) { sar *D = NULL;
132
} // Student srao spausdinimas void student::Spausdinti(int kaip) { sar *D = P; while(D) { if (kaip) cout << D->st->pavard << " " << D->st->vardas << " " << D->st->vidur << "\n"; else cout << D->vd->pavard << " " << D->vd->vardas << " " << D->vd->vidur << "\n"; D = D->next; } cout << "--------------------\n"; } // Student srao rikiavimas void student::Tvarkymas(int kaip) { // kaip = 1 tvarko pagal abcl st rodykl // kaip = 0 tvarko pagal vidurk vd rodykl sar *R = P, *R1; studentas *k; while(R != NULL ) { R1 = R->next; while(R1 != NULL) { if (kaip) { if (strcmp(R1->st->pavard, R->st->pavard) < 0) { k = R->st; R->st = R1->st; R1->st = k; }} else if (R1->vd->vidur > R->vd->vidur) { k = R->vd; R->vd = R1->vd; R1->vd = k; } R1 = R1->next; } R = R->next; } }
while (!feof(F)) { D = new sar; D->st = new studentas; D->vd = D->st; fscanf(F, "%s%s%f", D->st->pavard, D->st->vardas, &D->st->vidur); D->next = P; P = D; }
2 pavyzdys. Tekstiniame faile Stud.dat turime student sra: pavard, vardas, paymi vidurkis. Yra trys kompiuteri klass, kurias priiri ir tvarko studentai. Reikia suformuoti student darbo klasse sra savaitei. Kasdien paskiriami trys studentai srao eils tvarka. 133
iedinis
sraas.
typedef struct studentas { char pavard[10]; char vardas[10]; float vidur; }; typedef struct sar { studentas *st; sar *next; }; // Klass apraymas class student { sar *P; // Srao pradia public: student() { P = NULL; }; // Konstruktorius void Formuoti(FILE *); // Formuoja sra void Spausdinti(); // Spausdina sra void Darbai(int, int); // Sraai darbui }; // Pagrindin programa void main() { FILE *D; student A; // Objekto apraymas clrscr(); D = fopen("stud.dat", "r"); if (D == NULL) { cout << "Failo Stud.dat nepavyko atidaryti!"; exit(1); } A.Formuoti(D); cout << "---------------------\n"; fclose(D); A.Spausdinti(); cout << "---------------------\n"; A.Darbai(3, 3); } // iedinio student srao formavimas void student::Formuoti(FILE *F) { sar *D = NULL; while(!feof(F)) { D = new sar;
134
} // Student srao spausdinimas void student::Spausdinti() { sar *D; if (P) { // Pirmojo spausdinimas D = P; cout << D->st->pavard << " " << D->st->vardas << " " << D->st->vidur << "\n"; D = D->next; while(D != P) { // Kit spausdinimas cout << D->st->pavard << " " << D->st->vardas << " " << D->st->vidur << "\n"; D = D->next; }} } // Darb sraas void student::Darbai(int Dien, int kiek) { sar *D = P; int i, k; if (!D) { cout << "Sraas tuias\n"; exit; } for(i=1; i<=Dien; i++) { cout << "Diena: " << i << "\n"; for(k=1; k<=kiek; k++) { cout << " " << D->st->pavard << " " << D->st->vardas << "\n"; D = D->next; }} }
D->st = new studentas; fscanf(F, "%s%s%f", D->st->pavard, D->st->vardas, &D->st->vidur); D->next = P; P = D; } if (P) { // Srao pabaigos paieka D = P; while(D->next) D = D->next; D->next = P; } // iedo sudarymas
135
34
56
78 NULL
Dvikryptis sraas gali bti laikomas dviej vienkrypi sra kompozicija, todl jo tvarkymo funkcijos yra panaios i sra tvarkymo funkcijas. Pagrindinis skirtumas yra tas, kad tvarkymo 136
operacijose reikia formuoti dvi rodykles. Be to, galima sudaryti universalias element terpimo ir alinimo operacijas, kurios tinka visiems srao elementams, ne tiktai galiniams. Pradioje sraas tuias. Todl pirmojo elemento rodykls P (pradia) ir galinio elemento rodykls G (galas) reikms turi bti nulins NULL. Tuias sraas yra inicializuojamas deklaruojant nulines jo rodykles:
sar *P = NULL, *G = NULL;
typedef struct sar { int *S; // sar *de; // sar *ka; }; // // Klass apraymas class DviKryptys { sar *P; // sar *G; // public: DviKryptys() { P = NULL; G = NULL; ~DviKryptys(); sar *GetP() { return P; }; sar *GetG() { return G; }; void Formuoti(FILE *); // void Spausdinti(); // void SpausdintiAtv(); // };
137
// Pagrindin programa void main() { FILE *D; DviKryptys A; // Klass DviKryptys objektas clrscr(); D = fopen("Duom.dat", "r"); if (D == NULL) { cout << "Failo Duom.dat nepavyko atidaryti"; exit(1); } A.Formuoti(D); fclose(D); A.Spausdinti(); cout << "***********\n"; A.SpausdintiAtv(); A.~DviKryptys();
} // Srao formavimas, raant pradi void DviKryptys::Formuoti(FILE *F) { sar *R; // Darbinis kintamasis if (!feof(F)) { // Pirmojo elemento prijungimas G = new sar; P = G; P->S = new int; fscanf(F, "%i", P->S); P->ka = NULL; P->de = NULL; } while (!feof(F)) { // Likusi element prijungimas R = new sar; R->S = new int; fscanf(F, "%i", R->S); R->ka = NULL; R->de = P; P->ka = R; P = R; } } // Spausdina sra nuo pradios void DviKryptys::Spausdinti() { sar *D = P; while(D != NULL) { cout << *D->S << "\n"; D = D->de; } }
138
// Spausdina sra nuo galo void DviKryptys::SpausdintiAtv() { sar *D = G; while(D != NULL) { cout << *D->S << "\n"; D = D->ka; } } // Destruktorius DviKryptys::~DviKryptys() { sar *D = P; while(P != NULL) { D = P; P = P->ka; delete(D->S); delete(D); } }
Rodykl P rodo sra, kuriame duomenys saugomi atvirktine tvarka, o rodykl G rodo sra tiesiogine tvarka. Norint sukeisti rodykli reikmes srao tvarkos poiriu, reikia modifikuoti funkcij Formuoti. Silome tai padaryti patiems ir patikrinti. Srao perirai tinka vienkrypio srao periros algoritmas. Sraas nagrinjamas panaudojant vien rodykl: P ir de lauko reikmes arba G ir ka lauko reikmes. Turime galimyb sra perirti tiesiogine ir atvirktine tvarka. Tai demonstruoja spausdinimui skirtos funkcijos.
Pirmos dvi situacijos atitinka vieno elemento prijungimo veiksmams srao formavimo procese, todl atskirai j neaptarsime. Kuriant funkcij, skirt naujo elemento terpimui, reikia patikrinti situacij ir atlikti jai skirtus veiksmus (prie pirm element, po paskutinio elemento arba srao viduje). Elemento terpimas srao viduje po elemento R yra tapatus veiksmams prie element R.
D->de = R; // Naujas elementas pamato savo naujus kaimynus
139
terpiant nauj element D prie rodykls R nurodom srao element, reikia suformuoti keturias naujas, punktyrinmis linijomis parodytas, rodykles (8.2 pav.), kurios turi pakeisti dvi element ryius apraanias rodykles. Pradioje reikia sutvarkyti naujo elemento ryius su bsimais kaimynais srae, o po to pakeisti srao element ryius naujj element. P
<12> <34> <56> <78>
NULL NULL
<999>
T = A.GetP(); A.Terpti(T, &x); // terpimas srao pradioje A.Terpti(T->de, &x); // terpimas prie antraj element. Jis privalo bti T = A.GetG(); Terpti(T, &y); // terpimas prie paskutin element // terpimas "prie" R element void DviKryptys::Terpti(sar *R, int *sk) { sar *D; D = new sar; // Naujas elementas D->S = sk; // Rodykl reikm
140
if (R == P) { D->de = P; D->ka = NULL; P->ka = D; P = D; } else { D->de = R; D->ka = R->ka; (R->ka)->de = D; R->ka = D; } }
NULL NULL R
8.3 pav. Elemento R paalinimas
Elemento alinimo funkcijoje atsimenama ir grinama rodykl duomenis, kuriuos buvo nuoroda alinamame elemente. is veiksmas iai funkcijai yra perteklinis (nebtinas), nes atliekant alinamo elemento paiek yra nagrinjami duomenys, o tai reikia, kad juos jau turime.
R>de>ka = R>ka; R>ka>de = R>de;
141
alinimo funkcijos Mesti pavyzdyje nagrinjamos visos trys situacijos, bet nenagrinjama situacija, kai sraas tuias ir ar alinamo elemento rodykl netuia. To daryti nebtina, nes programos dalyje, kurioje nagrinjami paalinimo i srao kandidatai, btina patikrinti, ar toks elementas egzistuoja. Silome papildyti pavyzdio program ia funkcija ir patikrinti jos darb.
int *Mesti(sar *); // Metodo prototipas
T = A.GetP(); A.Mesti(T); // alinamas pirmas elementas T = A.GetG(); A.Mesti(T); // alinamas paskutinis elementas T = A.GetP(); A.Mesti(T->de); // alinamas antras elementas // alinti R element int *DviKryptys::Mesti(sar *R) { int *sk = R->S; if (R == P) { // Jeigu srao pradioje P = P->de; if (P != NULL) P->ka = NULL; } else if (R == G) { // Jeigu srao pabaigoje G = G->ka; if (G != NULL) G->de = NULL; } else { // Jeigu srao viduje R->de->ka = R->ka; R->ka->de = R->de; } delete(R); return sk; }
142
surandamas reikalingas elementas. Tik vis sra perirjus galima suinoti, kad neradome reikalingo elemento. Srao duomenys rakto atvilgiu yra sutvarkyti. Paieka vykdoma nuosekliai perirint elementus. Paieka nutraukiama suradus tinkam element arba susidarius situacijai, kai tolesnis srao nagrinjimas yra beprasmis: jau tikrai toliau srae negali bti iekomas elementas. Pavyzdiui, telefon knygoje, iekant abonento pagal pavard, nebtina perskaityti viso srao iki galo.
Duomen atrankos veiksmai priskirtini naujo srao formavimui, nes yra sudaromas naujas nuorod jau turimus duomenis sraas. Pavyzdiui, turime student sra. Galima sudaryti nuorod sra tik pirmo kurso studentus. Rikiavimo pagal duot rakt funkcijos yra analogikos funkcijoms, skirtoms darbui su vienkrypiu srau. ia reikia nepamirti, kad sraas dvikryptis ir reikia koreguoti dvi rodykles elementui. io tipo srauose veiksm su rodyklmis padvigubja, taiau paiekai skirt rodykli sumaja. Jeigu rikiavime leidiama keisti vietomis duomenis, tuomet naudojamos tik vienos krypties rodykls (pradios arba pabaigos). Jeigu reikia tvarkyti nuorodas, tuomet veiksmai tampa komplikuoti. Problema isprendiama, kuomet srae saugomi ne duomenys, o nuorodos juos. Rikiavimo metu keiiame vietomis tas nuorodas. Duomenys savo padties nekeiia. Tai leidia formuoti atskirus nuorod, tvarkingus pagal vairius poymius, sraus. Pavyzdiui, turime telefon abonent sra. Galima sudaryti nuorod sra pagal pavardes, abcls tvarka. Galima sudaryti sra, kuriame bt nuorodos t pat abonent sra, tik telefon numeri didjimo seka. Dvikryptis sraas gali bti naudojamas su tam tikrais apribojimais. Tai dvigubo steko bei eils tipo sraai. Vartotinas dvigubo iedo sraas, nes jame terpimo bei alinimo veiksmai nesudtingi, be to paprastas srao skenavimas pirmyn-atgal. Dirbant su srainmis struktromis, visuomet btina tikrinti nagrinjamo elemento padt srae: kratinis ar viduje. To galima ivengti, jeigu srao kratinius elementus tursime fiktyvius, t.y. jie nebus susieti su duomenimis, atliks buferini element funkcij. Tuias sraas bus tuo atveju, kai bus tik tie elementai. Vienkrypiame srae paprastai toks elementas formuojamas pradioje, taiau eils atveju j naudinga turti ir gale. Dvikryptis sraas paprastai turi du kratinius tuius elementus, 143
nors pakanka ir vieno. Tie elementai gali bti pastovs arba formuojami laikinai, kol bus atlikti atitinkami veiksmai, po to jie alinami.
typedef struct studentas { char pavard[10]; char vardas[10]; float vidur; }; typedef struct sar { studentas *st; sar *de; sar *ka; }; // Klass Studentai apibrimas class Studentai { sar *P; // sar *G; // public: // Studentai() { P = NULL; G = NULL; } void Formuoti(FILE *); // void Spausdinti(); // void Tvarkymas(float); // }; // Pagrindin programa void main() { FILE *D; Studentai A; // Klass clrscr();
Srao pradia Srao galas Konstruktorius Formuoja sra Spausdina sra Tvarkymas
Studentai objektas
144
} // Student srao formavimas void Studentai::Formuoti(FILE *F) { sar *R; if (!feof(F)) { R = new sar; R->st = new studentas; fscanf(F, "%s%s%f", R->st->pavard, R->st->vardas, &R->st->vidur ); R->de = NULL; R->ka = NULL; P = R; G = R; } while(!feof(F)) { R = new sar; R->st = new studentas; fscanf(F, "%s%s%f", R->st->pavard, R->st->vardas, &R->st->vidur); R->ka = G; G->de = R; R->de = NULL; G = R; } } // Srao spausdinimas void Studentai::Spausdinti(){ sar *D = P; while(D != NULL) { cout << D->st->pavard << " " << D->st->vardas << " " << D->st->vidur << "\n"; D = D->de; } } // alinimas i srao void Studentai::Tvarkymas(float vd) { sar *R, *D; R = new sar; // "Tuias" elementas srao pradioje
D = fopen("stud.dat", "r"); if (D == NULL) { cout << "Failo Stud.dat nepavyko atidaryti "; exit(1); } A.Formuoti(D); fclose(D); A.Spausdinti(); cout << "---------------------\n"; A.Tvarkymas(4.5); A.Spausdinti();
145
R->ka = NULL; R->de = P; R->st = NULL; P->ka = R; P = R; R = new sar; // "Tuias" elementas srao gale R->de = NULL; R->ka = G; R->st = NULL; G->de = R; G = R; R = P->de; while(R->de != NULL) { // alinimo ciklas if (R->st->vidur < vd) { R->de->ka = R->ka; R->ka->de = R->de; D = R; R = R->ka; delete(D->st); // alinimas rodykls student delete(D); } // alinimas srao elementas R = R->de; } R = P; // "Tuio" elemento alinimas i srao pradios P = R->de; P->ka = NULL; delete(R); // "Tuio" elemento alinimas i srao galo R = G; G = R->ka; if(G) G->de = NULL; else P = NULL; // Sraas tuias delete(R); }
146
9.1 pav. Daugiasrain struktra, organizuojama masyvu. Sra raktiniai odiai ir rodykls sraus saugomos masyve
Lankstesn struktra gaunama, kai vietoje masyvo yra formuojamas nuoseklus sraas, vienkryptis arba dvikryptis (9.2 pav.). Tokio srao elementas turi rodykl duomen tiesin sra ir srao pavadinimo lauk (arba rodykl t pavadinim). Gauname akot sra, kurio struktra universali duomen atvilgiu: nereikia modifikuoti program papildant nauju duomen srau. Tinka visi veiksmai, aptarti tiesiniams sraams. Priklausomai nuo pradini duomen funkcins tarpusavio priklausomybs ir planuojam apdorojimo veiksm yra projektuojama srain struktra. J tikslinga parinkti toki, kad veiksmai bt kuo paprastesni. Kurdami akot struktr mes i anksto numatome tam tikr duomen suskirstym, klasifikavim. Gerai suprojektuota struktra supaprastina veiksmus. 147
P NULL IF FM TT HM
148
typedef struct studentas { char pavard[10]; char vardas[10]; char fakult[10]; float vidur; }; typedef struct sar { studentas st; sar *kitas; }; typedef struct fakultetas { char fak[10]; fakultetas *sek; sar *stud; }; // Klass Uni apraymas class Uni { fakultetas *P; // Dinaminio srao pradia public: Uni() { P = NULL; }; // Konstruktorius void Formuoti(FILE *); // Formuoja sra void Spausdinti(sar *); // Spausdina sra void Visi_stud(); void Pasirink(); }; // Pagrindin programa void main() { FILE *D; Uni A; // Klass Uni objektas clrscr(); D = fopen("studf.dat", "r"); if (D == NULL) { cout << "Failo Studf.dat nepavyko atidaryti "; exit(1); } A.Formuoti(D); fclose(D); A.Visi_stud(); A.Pasirink();
149
// Srao formavimas void Uni::Formuoti(FILE *F) { fakultetas *R; sar *A; int yra; while(!feof(F)) { A = new sar; fscanf(F, "%s%s%s%f", A->st.pavard, A->st.vardas, A->st.fakult, &A->st.vidur); yra = 0; R = P; while(!yra && R) if (strcmp(A->st.fakult, R->fak) == 0) else R = R->sek; if (!yra) { R = new fakultetas; R->stud = NULL; strcpy(R->fak, A->st.fakult); R->sek = P; P = R; } A->kitas = R->stud; R->stud = A; }
yra = 1;
} // Spausdina vieno fakulteto student sra void Uni::Spausdinti(sar *R) { while(R) { cout << R->st.pavard << " " << R->st.vardas << " " << R->st.vidur << "\n"; R = R->kitas; } cout << "------------------------\n"; } // Spausdina vis fakultet student sraus void Uni::Visi_stud() { fakultetas *R = P; cout << "=== Fakultet ir student sraai ===\n\n"; while(R) { cout << "Fakultetas " << R->fak << ":\n"; Spausdinti(R->stud);
150
} // Pageidaujamo fakulteto student srao spausdinimas void Uni::Pasirink() { fakultetas *R; char fk[10], s; int yra, rinkti = 1; while(rinkti) { cout << "Fakultetas= "; cin >> fk; yra = 0; R = P; while(!yra && R) if (strcmp(fk, R->fak) == 0) else R = R->sek;
R = R->sek;
yra = 1;
if (yra) { cout << R->fak << ":\n"; Spausdinti( R->stud ); } else cout << fk << " srae nerastas\n"; cout << "------------------------\n"; cout << "Ar dar renkate fakultet(T)?"; cin >> s; if ((s != 'T') && (s != 't')) rinkti = 0;
} }
151
D0
0-is lygis
D11
D12
D13
1-sis lygis
D21
D22
D23
D24
D25
2-sis lygis
152
Apraant programose medio tipo struktras, yra paprastai ribojamas nuorod emesnius hierarchijos lygius (rodykli) skaiius. Jei jis apribojamas dviem, tai toks medis yra vadinamas binariniu. Binarini medi elementai yra apraomi taip pat, kaip ir dvikrypi sra elementai, ryio dalyje formuojant dvi nuorodas. Tiktai priimta ias nuorodas ymti kitokiais vardais, pabriant, kad medio akos yra nukreiptos dein arba kair pus (de, ka):
struct medis { void *data; medis *ka; medis *de; } // Duomen rodykl // Kairs medio akos rodykl // Deins medio akos rodykl
Binariniai mediai labai gerai tinka organizuoti tokioms saugojimo struktroms, i kuri danai tenka pagal poymius atrinkinti pavienius elementus arba nedideles j grupes. Apraant medio tipo struktras, taip pat reikia apibrti j formavimo, tvarkymo bei apdorojimo procedras.
10.2. Formavimas
Formavimo veiksmai priklauso nuo srao struktros ir duomen tarpusavio priklausomybs. Universali paprogrami negalima sudaryti.
Duomen rinkinys: 5 8 3 1 4 6 3 7 5
8
NULL
1
NULL NULL
4
NULL NULL
3
NULL NULL NULL
7
NULL
153
Medio formavim iliustruosime tokiu pavyzdiu. Tarkime, kad reikia sudaryti med, kuriame bt saugomi elementai, turintys sveik skaii reikmes. Skaiiai gaunami i failo, kuriame jie surayti atsitiktine tvarka. Medyje juos reikia patalpinti didjimo tvarka (10.2 pav.). Formuosime binarin med. Tai bus padaryta taip. Pirmas medio elementas turs pirmojo skaiiaus reikm. Kitiems skaiiams bus sukuriami elementai, kuri vieta medyje bus randama tokiu dsniu: jeigu nauja reikm yra maesn u nagrinjamo medio elemento saugom reikm, tai pereiname prie kairiojo elemento, kitaip prie deiniojo. Naujas elementas pakabinamas medyje toje vietoje, kur randamas akos galas, t.y. elementas su rodykls reikme NULL. Naujas elementas su nauja reikme turi turti ka ir de reikmes NULL.
// #include #include #include #include Medio sudarymo pavyzdys <iostream.h> <stdio.h> <stdlib.h> <conio.h>
typedef struct medis { int sk; medis *de; medis *ka; }; // Klass Binmedis apraymas class Binmedis { medis *P; // Medio virn public: Binmedis() { P = NULL; }; // Konstruktorius void Formuoti(FILE *); // Formuoja med medis *Get() { return P; }; void Padeti(medis *); // Naujo elemento vieta medyje void Spausdinti(); // Spausdina reikmes didjimo tvarka // Spausdina reikmes majimo tvarka void SpausdintiRek(medis *); medis *Rasti(int); // Duotos reikms paieka medyje }; // Pagrindin programa void main() { FILE *D; medis *R; Binmedis A; // Objekto apraymas clrscr(); D = fopen("duom.dat", "r");
154
if (D == NULL) { cout << "Failo Duom.dat nepavyko atidaryti "; exit(1); } A.Formuoti(D); fclose(D); A.Spausdinti(); // Didjimo tvarka cout << "\n" << "======================\n"; R = A.Get(); A.SpausdintiRek(R); // Majimo tvarka cout << "\n" << "======================\n"; for(int i=0; i<=10; i++) { // Paiekos iliustracija R = A.Rasti(i); if (R) cout << i << " Radau!\n"; else cout << i << "* neradau\n"; }
} // Medio formavimas void Binmedis::Formuoti(FILE *F) { medis *R; while(!feof(F)) { // Naujas elementas R = new medis; R->ka = NULL; R->de = NULL; fscanf(F, "%i", &R->sk); if (!P) P = R; // Elementas dedamas med else Padeti(R); } } // Padti element medyje void Binmedis::Padeti(medis *R) { medis *D = P, *T; while(D) { T = D; // Tuios akos paieka if (R->sk < D->sk) D = D->ka; else D = D->de; } if (R->sk < T->sk) T->ka = R; // Elemento prijungimas else T->de = R; } // Spausdinimas didjimo tvarka void Binmedis::Spausdinti() { medis *R = P; struct stekas {medis *S; // Stekas medio trasavimui stekas *sek; } *A = NULL, *D;
155
} // Spausdinimas majimo tvarka (su rekursija) void Binmedis::SpausdintiRek(medis *R) { if (R) { SpausdintiRek(R->de); cout << R->sk << " "; SpausdintiRek(R->ka); } } // Elemento, turinio nurodyt reikm paieka medis *Binmedis::Rasti(int Sk) { medis *R = P; while(R && (R->sk != Sk)) if (Sk < R->sk) R = R->ka; else R = R->de; return R; // Neradus grinama reikm NULL }
while(R || A) { if (R) { D = new stekas; D->sek = A; A = D; A->S = R; R = R->ka; } else { R = A->S; D = A; A = A->sek; delete(D); cout << R->sk << " R = R->de; } }
";
10.3. Perira
Perrenkant medyje esanias reikmes yra vadovaujamasi mediui sukurti naudotais principais. Pavyzdio med galima perirti skaii didjimo arba majimo tvarka. Perira pradedama nuo medio virns ir iekome toliausiai kairje esanios akos virns, kurios reikm bus maiausia. J spausdiname ir pasukame artimiausi dein ak, nuo kurios vl iekome toliausiai kair pus esanios virns. T.y. kartojame medio formavimo proces. Kadangi mediu galime judti tik viena kryptimi (nra nuorod auktesn lyg), tai btina atsiminti virnes, per 156
kurias buvo nusileidiama iki atitinkamos virns. Tam naudojamas stekas, kur galima organizuoti masyve, tiesiniu srau arba ireikti netiesiogiai panaudojant rekursin paiekos bd. Pavyzdio programoje yra funkcija Spausdinti, kuri skirta spausdinti medio reikmes didjimo tvarka ir kuri stekui naudoja tiesin sra. Funkcija SpausdintiRek skirta spausdinti medio saugomas reikmes majimo tvarka ir yra rekursin. Iekant medyje duotos reikms yra vadovaujamasi vienu i medio periros bd: kairiniu arba deininiu. Pavyzdio programoje funkcija Rasti demonstruoja paiek kairiniu bdu. i funkcij galima patobulinti: duomenys yra tvarkingai sudti, todl paiek galima nutraukti, kuomet tolesn perira tampa beprasm (iekoma reikm tampa maesne u aktyvaus elemento saugom reikm). Binarini medi skleidimu yra vadinamas vis j element perrinkimas. Nuo perrinkimo bdo priklauso, kokia tvarka medyje saugomi duomenys yra perduodami juos apdorojanioms funkcijoms. Naudojami trys pagrindiniai skleidimo bdai: kairysis, deinysis ir centrinis. Naudojant kairj skleidimo bd, kiekviename elemente i pradi yra perrenkama kairiosios rodykls rodoma medio aka. Po to apdorojami elemento saugomi duomenys ir perrenkama deinioji aka. Skleidimas yra pradedamas nuo medio kamieno. Deinysis skleidimas vykdomas prieinga kairiajam skleidimui tvarka, o centrinio skleidimo atveju, i pradi apdorojamas duomen elementas ir tik po to perrenkamos kairioji ir deinioji akos. Visi aptarti skleidimo bdai yra labai glaustai apraomi rekursinmis funkcijomis. Pavyzdiu gali bti pavyzdyje pateiktos medyje saugom reikmi spausdinimas.
157
158
Rezultat pavyzdys. Rezultatai lenteli formoje kaupiami tekstiniame faile vardu Kelione.rez (11.2 pav.). Pavyzdyje yra duomen ir pasirinkimui tinkam kelioni lentels. Pasirinkimo sraas yra surikiuojamas pagal trukm ir kain.
Siulomu kelioniu sarasas +----+------------+--------+------------+---------+ | Nr.| Keliones t.+ Trukme + Vieta + Kaina + +----+------------+--------+------------+---------+ | 1 | Valtis | 12 | Neris | 345 | | 2 | Joti | 5 | Utena | 230 | | 3 | Laivas | 25 | Afrika | 2500 | | 4 | Traukinys | 15 | Turkija | 1200 | | 5 | Eiti | 10 | Dzukija | 100 | | 6 | Dviratis | 15 | Lietuva | 200 | +----+------------+--------+------------+---------+ Atrinktu kelioniu sarasas +----+------------+--------+------------+---------+ | Nr.| Keliones t.+ Trukme + Vieta + Kaina + +----+------------+--------+------------+---------+ | 1 | Joti | 5 | Utena | 230 | | 2 | Eiti | 10 | Dzukija | 100 | | 3 | Valtis | 12 | Neris | 345 | | 4 | Dviratis | 15 | Lietuva | 200 | | 5 | Traukinys | 15 | Turkija | 1200 | +----+------------+--------+------------+---------+ 11.2 pav. Rezultat failo pavyzdys
Programoje sukurta klas Kelions. Duomen srao rodykl P, atrinkt kelioni srao rodykl K. 159
Pagrindin funkcija. P duomen srao rodykl. K rezultat srao rodykl. F failinis kintamasis. ia atidaromi ir udaromi failai, formuojamas duomen sraas P ir rezultat sraas K, rikuojamas sraas K, spausdinami sra duomenys lentelmis. Veiksmams atlikti naudojami padaryti metodai. void Keliones::Formuoti(FILE *F); Funkcijai perduodamas atidarytas duomen failas F. ia skaitomi duomenys po vien eilut ir formuojamas duomen sraas. Srao pradia P. Sraas formuojamas steko principu. Panaudojama funkcija Padeti void Keliones::Padeti(sar **P, kelione N); Sukuria nauj element su duotos kelions N duomenimis ir prijungia prie srao P pradios. void Keliones::Spausdinti(sar *P, FILE *F); Srao P saugomus duomenis lentels forma surao tekstin fail F. void Keliones::Rikiuoti(sar *P); Srao P duomenis surikiuoja pagal nurodyt rakt, aprayt funkcijoje Raktas. Sraas rikiuojamas daliniu Minmax bdu. Rikiavimo metu duomenys keiiami vietomis. int Keliones::Raktas(kelione A, kelione B); Palyginami dviej kelioni A ir B duomenys. Jeigu B kelions trukm maesn u A arba, kai trukms vienodos, B kelions kaina yra didesn u A kelions kain, tai grinama reikm 1 (reikia sukeisti vietomis A su B), kitaip grinama reikm 0. void Keliones::Rinkti(); ia keliautojo paklausiama didiausia galima kelions kaina ir ilgiausia galima trukm. I srao P atrenkami tinkami duomenys ir suformuojamas naujas sraas K. Sraas formuojamas steko principu. Naudojama funkcija Padeti. void Keliones::Naikinti(sar *P); Funkcija paalina sra P i atminties.
160
sar *Keliones::GetP() { return P; }; Grina vis duomen srao pradios rodykl P. sar *Keliones::GetK() { return K; }; Grina atrinkt duomen srao pradios rodykl K.
Programos tekstas
struct kelione { char tipas[10]; char vieta[10]; int trukme; int kaina; }; typedef struct sar { kelione S; sar *sek; }; // Klass Keliones apraas class Keliones { sar *P; // Vis kelioni srao pradia sar *K; // Atrinkt kelioni sraas public: Keliones() { P= NULL; K= NULL; };// Konstruktorius void Formuoti(FILE *); // Formuoja sra void Padeti(sar **, kelione); sar *GetP() { return P; }; sar *GetK() { return K; }; void Spausdinti(sar *, FILE *); // Spausdina sra void Rikiuoti(sar *); // Rikiuoja int Raktas(kelione, kelione); // Rikiavimo raktas void Rinkti(); // Atrenkami duomenys void Naikinti(sar *); // Srao alinimas }; // Pagrindin programa void main() { FILE *F; Keliones A; // Klass Keliones objektas sar *T; // Pagalbinis kintamasis clrscr(); cout << "Programa darba pradejo\n\n";
161
F = fopen("Kelione.sar", "r"); if (F == NULL) { cout << "Failo 'Kelione.dat' nepavyko atidaryti "; exit(1); } A.Formuoti(F); fclose(F); F = fopen("Kelione.rez", "w"); if (F == NULL) { cout << "Failo 'Kelione.rez' nepavyko sukurti"; exit(1); } fprintf(F, "Siulomu kelioniu sarasas\n\n"); T = A.GetP(); A.Spausdinti(T, F); A.Rinkti(); T = A.GetK(); A.Rikiuoti(T); fprintf(F, "Atrinktu kelioniu sarasas\n\n"); T = A.GetK(); A.Spausdinti(T, F); fclose(F); T = A.GetP(); A.Naikinti(T); T = A.GetK(); A.Naikinti(T); cout << "Pabaiga. Rezultatai faile 'Kelione.rez'";
} // Elemento raymas void Keliones::Padeti(sar **P, kelione N) { sar *D = new sar; D->S = N; D->sek = *P; *P = D; } // Srao formavimas void Keliones::Formuoti(FILE *F) { kelione S; do { fscanf(F, "%s%i%10s%i", S.tipas, &S.trukme, S.vieta, &S.kaina); Padeti(&P, S); }
162
while(!feof(F)); } // Srao spausdinimas void Keliones::Spausdinti(sar *P, FILE *F) { sar *D = P; int i = 1; fprintf( F,"+----+------------+--------+------------"); fprintf( F,"+---------+\n" ); fprintf( F,"| Nr.| Keliones t.+ Trukme + Vieta "); fprintf( F,"+ Kaina +\n" ); fprintf( F,"+----+------------+--------+------------"); fprintf( F,"+---------+\n" ); while(D) { fprintf( F,"| %2d | %10s | %6d | %10s | %6d |\n", i++, D->S.tipas, D->S.trukme, D->S.vieta, D->S.kaina); D = D->sek; } fprintf( F,"+----+------------+--------+------------"); fprintf( F,"+---------+\n" ); fprintf( F, "\n\n\n"); } // Rikiavimo raktas int Keliones::Raktas(kelione A, kelione B) { return(B.trukme < A.trukme) || ((B.trukme == A.trukme ) && (B.kaina > B.kaina)); } // Rikiavimas void Keliones::Rikiuoti(sar *P) { sar *R = P, *D; kelione K; while(R) { D = R->sek; while(D) { if (Raktas(R->S, D->S)) { K = R->S; R->S = D->S; D->S = K; } D = D->sek; } R = R->sek; } } // Duomen atrinkimas void Keliones::Rinkti() { sar *D = P; int Sk1, Sk2; cout << "Kokia maksimali kaina ? "; cin >> Sk1; cout << "\n"; cout << "Kokia maksimali trukme? ";
163
} // Srao paalinimas void Keliones::Naikinti(sar *P) { sar *R = P, *D; while(R) { D = R; R = R->sek; delete(D); } }
cin >> Sk2; cout << "\n"; while(D) { if ((D->S.trukme <= Sk2) && (D->S.kaina <= Sk1)) Padeti(&K, D->S); D = D->sek; }
11.2. Uduotys
U1. Turime tekstiniame faile meteorologini stebjim rezultatus: data, krituli pobdis, krituli kiekis. Suformuoti sra: krituli pobdis, bendras kiekis, vidurkis. Sutvarkyti duomenis pagal krituli pobd ir krituli kiek. Ivesti tris lenteles: duomen, sutvarkyt duomen ir skaiiavim rezultat. U2. Turime tekstiniame faile darbuotoj iniarat: pavard, adresas, darbo pavadinimas, data, dirbta valand. Kitame tekstiniame faile turime darb vertinimo normatyvus: darbo pavadinimas, valandos kainis. Suformuoti atlyginim sra ir j sutvarkyti pagal atlyginimo dyd ir pavard: pavard, adresas, dirbtas valand kiekis, atlyginimas. Ivesti tris lenteles: dvi duomen ir skaiiavim rezultat. U3. Turime tekstiniame faile bibliotekos lankytoj sra: pavard, vardas, skaitytojo bilieto numeris, knyg pamimo data, paimt knyg kiekis, leistinas skaityti dien skaiius. Suformuoti skaitytoj, kurie vluoja grinti knygas, sra, ir j sutvarkyti pagal vlavim ir pavard: pavard, vardas, skaitytojo bilieto numeris, vluojamas dien skaiius, knyg kiekis. Ivesti dvi lenteles: duomen ir skaiiavim rezultat. 164
U4. Turime tekstiniame faile student sra: pavard, vardas, grups ifras, egzamin pavadinimai ir paymiai. Suformuoti pirmn, kuri paymi vidurkis yra duotame intervale [a, b], sra ir j sutvarkyti pagal grupes ir pavardes: pavard, vardas, grups ifras, paymi vidurkis. Ivesti dvi lenteles: duomen ir skaiiavim rezultat. U5. Turime tekstiniame faile student sra: pavard, vardas, grups ifras, vis egzamin paymiai. Suformuoti sra: pavard, vardas, grups ifras, paymi vidurkis. J sutvarkyti pagal grups ifr ir paymi vidurk. Ivesti dvi lenteles: duomen ir skaiiavim rezultat. U6. Turime tekstiniame faile student sra: pavard, vardas, grups ifras, vis egzamin paymiai. Suformuoti sra: pavard, vardas, grups ifras, kiek koki paymi turi. J sutvarkyti pagal grups ifr ir paymi vidurk. Ivesti dvi lenteles: duomen ir skaiiavim rezultat. U7. Turime tekstiniame faile student sra: pavard, vardas, grups ifras, vis egzamin paymiai. Suformuoti sra: pavard, vardas, grups ifras, mokymosi vidurkis. J sutvarkyti pagal grups ifr ir mokymosi vidurk. Ivesti lenteles: duomen ir skaiiavim rezultat, pirmn (mokosi vidurkiu >8) ir atsiliekani (mokosi vidurkiu <6). U8. Turime tekstiniame faile student sra: pavard, vardas, grups ifras, vis egzamin paymiai. Suformuoti sra: pavard, vardas, grups ifras, atuntuk,devintuk ir deimtuk kiekiai. J sutvarkyti pagal atuntuk, devintuk, deimtuk kiek. Ivesti lenteles: duomen ir skaiiavim rezultat. U9. Turime tekstiniame faile student darbo prie kompiuteri apskaitos sra: pavard, vardas, grups ifras, kompiuterio registracijos numeris, data, darbo pradios laikas, darbo pabaigos laikas.
165
Suformuoti sra: pavard, vardas, grups ifras, kompiuterio registracijos numeris, data, darbo laikas minutmis. J sutvarkyti pagal dirbt laik ir pavard. Ivesti dvi lenteles: duomen ir skaiiavim rezultat. U10. Turime tekstiniame faile student apskaitos sra: pavard, vardas, stojimo Universitet data, vis egzamin paymiai. Suformuoti sra: pavard, vardas, kursas, atuntuk, devintuk, deimtuk kiekiai. J sutvarkyti pagal deimtukus, devintukus, atuntukus. Ivesti dvi lenteles: duomen ir skaiiavim rezultat. U11. Turime tekstiniame faile student sra: pavard, vardas, grups ifras, gimimo data, vis egzamin paymiai. Suformuoti sra: pavard, vardas, amius, kiek koki paymi turi. J sutvarkyti pagal ami ir mokymosi vidurk. Ivesti dvi lenteles: duomen ir skaiiavim rezultat. U12. Turime tekstiniame faile viebuio gyventoj sra: pavard, vardas, atvykimo data, ivykimo data, kaina u par. Suformuoti sra: pavard, vardas, atvykimo data, ivykimo data, mokestis. Duomenis ir rezultatus sutvarkyti pagal atvykimo dat. Ivesti dvi lenteles: duomen ir skaiiavim rezultat. U13. Turime tekstiniame faile viebuio gyventoj sra: pavard, vardas, atvykimo data, ivykimo data, paros kaina. Suformuoti sra: pavard, vardas, atvykimo data, gyventa par, mokestis J sutvarkyti pagal gyvenimo trukm ir mokest. Ivesti dvi lenteles: duomen ir skaiiavim rezultat. U14. Turime tekstiniame faile viebuio gyventoj sra: pavard, vardas, atvykimo data, gyventa par, paros kaina. Suformuoti sra: pavard, vardas, atvykimo data, ivykimo data, mokestis. J sutvarkyti pagal pavardes ir vardus. Ivesti dvi lenteles: duomen ir skaiiavim rezultat. U15. Turime tekstiniame faile viebuio gyventoj sra: pavard, vardas, atvykimo data, gyventa par, kambario tipas. 166
Kitame tekstiniame faile turime kainorat: kambario tipas, paros kaina. Suformuoti sra: pavard, vardas, atvykimo data, ivykimo data, mokestis. J sutvarkyti pagal mokest. Ivesti dvi lenteles: duomen ir skaiiavim rezultat. U16. Turime knyg sra: autorius, pavadinimas, ileidimo metai, tiraas, kaina. Suformuoti sra: autorius, pavadinimas, ileidimo metai, tirao pinigin vert. Sra sutvarkyti pagal ileidimo dat. Ivesti lenteles: duomen ir skaiiavim rezultat. U17. Turime tekstiniame faile viebuio gyventoj sra: pavard, vardas, atvykimo data, gyventa par, kambario tipas. Kitame tekstiniame faile turime kainorat: kambario tipas, kaina, gyvenimo kaina, buitini paslaug kaina, pelno procentas. Suformuoti sra: pavard, vardas, atvykimo data, gyventa par, mokestis. J sutvarkyti pagal atvykimo dat. Ivesti dvi lenteles: duomen ir skaiiavim rezultat. U18. Turime sra: detal, pagaminimo data, kiekis. Kitame tekstiniame faile turime kainorat: detal, gamybos ilaid kaina, pelno procentas. Suformuoti sra: detal, pardavimo kaina, kiekis. J sutvarkyti pagal kain ir kiek. Ivesti dvi lenteles: duomen ir skaiiavim rezultat. U19. Turime sra: detal, pagaminimo data, kiekis. Kitame tekstiniame faile turime kainorat: detal, mediag kaina vienete, darbo snaudos vienetui, kitos papildomos ilaidos vienos detals gamybai. Suformuoti sra: detal, kiekis, pardavimo kaina, priskaiiuotas atlyginimas. J sutvarkyti pagal kiek ir atlyginim. Ivesti dvi lenteles: duomen ir skaiiavim rezultat. 167
U20. Turime sra: detal, parduotas kiekis, pardavimo data. Kitame tekstiniame faile turime kainorat: detal, kaina. Suformuoti sra: detal, pardavimo data, pardavimo kaina, suma. J sutvarkyti pagal dat ir kain. Ivesti dvi lenteles: duomen ir skaiiavim rezultat. U21. Turime sra: prek, gautas kiekis, parduotas kiekis, pardavimo data. Kitame tekstiniame faile turime kainorat: prek, kaina. Suformuoti sra: prek, likutis, pardavimo kaina, pajamos. J sutvarkyti pagal kain ir pajamas. Ivesti dvi lenteles: duomen ir skaiiavim rezultat. U22. Turime sra: prek, pagaminimo data, vartojimo laikas. Suformuoti sra: prek, pagaminimo data, vartojimo laikas, vartojimo pabaigos data. J sutvarkyti pagal pagaminimo dat ir pavadinim. Ivesti dvi lenteles: duomen ir skaiiavim rezultat.
168
169