Amallar. Amallar bir yoki bir nechta belgilar bilan aniqlanadi va operatorlar ustida bajariladi. Amal orasida bo`sh joy qo`yilmaydi. Amaldagi belgilar maxsus belgilardan (masalan, &&, |, <) va harflardan (masalan, reinterpret_cast, new) iborat bo`lishi mumkin.
1.2. Xotirani zahiralash turlari Statik ob’yektlar nomlangan o‘zgaruvchilar bilan belgilanadi. Shu sababli bunday ob’yektlar ustida amallar bevosita ularning nomlarini ishlatish orqali amalga oshiriladi. Dinamik ob’yektlar shaxsiy nomga ega bo‘lmaydi, ular ustidagi amallar bilvosita, ya’ni ko‘rsatkichlar yordamida bajariladi;Statik ob’yektlar uchun xotira ajratish va bo‘shatish kompilyator tomonidan avtomatik ravishda bajariladi. Dasturchining bu haqda bosh qotirishi shart emas.Dinamik ob’yektlar uchun xotira ajratish va bo‘shatish butunligicha dasturchining zimmasiga tushadi. Bu esa hal qilishda xatoga yo‘l qo‘yish mumkin bo‘lgan murakkab masaladir.Dinamik ajratilgan xotira bilan ish ko‘rish uchun new va deletebuyruqlaridan foydalaniladi.Shu paytgacha barcha misollarda xotirani statik ajratish usulidanfoydalanilib kelindi. Masalan, i o‘zgaruvchini aniqlash quyidagicha bajarilar edi:
int i = 1024;
Bu buyruq xotirada int turidagi o‘zgaruvchini saqlash uchun zarur bo‘lgan soha ajratadi, bu sohani I nom bilan bog‘laydi va u joyga 1024 qiymatni yozib qo‘yadi. Bularning barchasi dasturning bajarilishigacha bo‘gan kompilyatsiya paytida bajariladi.O‘zgaruvchi uchun joy ajratishning yana bir usuli mavjud bo‘lib, u new buyrug‘ini ishlatish yordamida bajariladi.newbuyrug‘i ikkita shaklga egabo‘ladi. Birinchi shaklda aniq turli birlik ob’yekt uchun joy ajratadi:
int *pint = new int(1024);
Bu yerda new buyrug‘i int turidagi nomsiz ob’yekt uchun joy ajratadi, uni 1024 qiymat bilan initsializatsiya qiladi va hosil qilingan ob’yektning manzilini uzatadi. Bu manzil pint ko‘rsatkichiga joylashtiradi.Bunday nomsiz ob’yektlar ustidagi barcha amallar mazkur ko‘rsatkich bilan ishlash orqali bajariladi, chunki dinamik ob’yektlar ustida bevosita ishlash mumkin emas.Ikkinchi shaklda new buyrug‘i ma’lum tur elementlaridan tashkil topgan, ko‘rsatilgan o‘lchamdagi massiv uchun xotira ajratadi:
int *pia = new int[4];
Bu misolda int turidagi to‘rt elementdan iborat massiv uchun xotira ajratiladi. Afsuski, new buyrug‘ining bu shaklida massiv elementlarini initsializatsiya qilish imkoniyati yo‘q.new buyrug‘ining ikkala shaklida ham bir xil ko‘rsatkich aniqlanishi ba’zi chalkashliklarga olib kelishi mumkin. Qaralayotgan misolda bu butun turga ko‘rsatkichdir. pint ham, pia ham bir xil e’lon qilingan, ammo pint int turidagi yagona ob’yektni ko‘rsatadi, pia esa int turidagi to‘rtta ob’yektdan iborat massivni ko‘rsatadi.Dinamik ob’yekt kerak bo‘lmay qolganda unga ajratilgan sohani oshkor ravishda bo‘shatish mumkin. Bu delete buyrug‘i yordamida bajariladi:
delete pint;
Massivni bo‘shatish ham new buyrug‘i kabi ikki shaklga ega, birlik ob’yekt uchun va massiv uchun.
delete[] pia;
Agar ajratilgan xotirani bo‘shatishni unutsak, u bo‘shga sarflanadi, ishlatilmaydi. Agar unga bo‘lgan ko‘rsatkich o‘z qiymatini o‘zgartirgan bo‘lsa uni tizimga qaytarish ham mumkin emas. Bundayxodisa xotiraning oqib ketishi degan maxsus nom olgan. Oxir oqibatda dastur xotira etmasligi sababli halokatli (avariyali) to‘xtaydi (agar u uzoq vaqt ishlasa).
«Ko‘rsatkichlardan foydalanuvchi ro‘yxatlar»
Dastur tasnifi Mazkur dasturda mustaqil ravishda dasturchilar tomonidan teztez ishlatiladigan, «Ikki yo‘nalishli ro‘yxat» konstruksiyasini tuzishga to‘g‘ri keladi. Bu konstruksiya nomer bo‘yicha tartiblangan strukturalar majmuini bildiradi. Unda har bir struktura o‘z tarkibida to‘plamning yaqin o‘zgaruvchilariga ikkitadan ko‘rsatkichga ega bo‘ladi. Tabiiyki, struktura bu ko‘rsatkichlardan tashqari dastur uchun ba’zi foydali ma’lumotlarga ham ega bo‘ladi.Bu misol uchun masala quyidagicha qo‘yiladi: Foydali ma’lumotlar sifatida belgilar qatorini qamrab oluvchi, ikki yo‘nalishli ro‘yxat hosil qilish imkoniga ega struktura, hamda ro‘yxatga elementlar qo‘shuvchi va ro‘yxatdan elementlarni o‘chiruvchi protseduralar tuzilsin. Bu protseduralar tugmachalar bilan biriktirilsin va ro‘yxatda saqlanuvchi ma’lumotlarni chiqarish ta’minlansin.Muammolar Dinamik xotira bilan ishlashda zaxiralangan ma’lumotlar blokining o‘z vaqtida bo‘shatilishini nazorat qilib borish, ya’ni bunday protsedurani nazarda tutish, zarur. Bundan tashqari, dasturni tugatishdanoldin xotiraning barcha zaxiralangan bloklari bo‘shatilishi kerak. Buninguchun «Shakllarni yopish» - «Закрытие формы» (FormClose) xodisalarni qayta ishlash protsedurasidan xotirani bo‘shatish protsedurasini chaqirish zarur.Zarur ko‘nikmalar Ikki yo‘nalishli ro‘yxat bilan ishlash uchun ko‘rsatkichlar va strukturalar bilan ishlash ko‘nikmalari zarur. Dasturda ko‘rsatkichga ko‘rsatkich bilan bog‘liq murakkab kostruksiyalar ishlatiladi (masalan,manzilini o‘z navbatida boshqa ko‘rsatkich saqlovchi ko‘rsatkich saqlanadigan manzilni o‘z ichiga oluvchi o‘zgaruvchiga biror qiymat o‘zlashtirishda). Bu dasturda “->” buyrug‘i ishlatiladi. Bu buyruq
strukturaning bir qismi bo‘lgan, manzili shu buyruq tadbiq etilayotgan ko‘rsatkich bo‘lgan o‘zgaruvchini aniqlaydi (qaytaradi). Agar buyruq aniqlagan o‘zgaruvchi strukturaga ko‘rsatkich bo‘lsa, u holda unga shu buyruqning o‘zi yana tadbiq qilinishi mumkin. Buning oqibatida ko‘p o‘tishlardan iborat uzun konstruksiyalar paydo bo‘ladi.
Yechish
Shakl Mazkur dasturning shaklida ro‘yxatning mazmunini ekranga chiqarish uchun maydon va element qo‘shish, elementni o‘chirish, barcha elementlarni aks ettirish yoki o‘chirish imkonini beruvchi barchafunksional tugmachalar joylashgan bo‘lishi kerak. Satrlarni chiqarish maydonini ma’noli birlashtirish maqsadida yangi element uchun «Dobavit» tugmachasi bilan birga shu tugmachaga mos nomli, hamkiritish maydoni, ham tugmachadan iborat guruh tuzilgan.C++ da dasturlashning asosiy bloklaridan biri ko'rsatkichlardir. Ko'rsatkichlarning foydasi shundaki, katta masala bir necha kichik bo`laklarga bo`linib, har biriga alohida funksiya yozilganda, masala yechish algoritmi ancha soddalashadi. Bunda dasturchi yozgan ko'rsatkichlar C++ ning standart kutubxonasi va boshqa firmalar yozgan kutubxonalar ichidagi ko'rsatkichlar bilan birlashtiriladi. Bu esa ishni osonlashtiradi. Ko`p holda dasturda takroran bajariladigan amalni funksiya sifatida yozish va kerakli joyda ushbu funksiyani chaqirish mumkin. Funksiyani programma tanasida ishlatish uchun u chaqiriladi, yani uning ismi yoziladi va unga kerakli argumentlar beriladi. () qavslar ushbu funksiya chaqirig`ini ifodalaydi. Masalan,
foo(); k = square(l); Demak, agar funksiya argumentlar olsa, ular () qavs ichida yoziladi. Argumentsiz funksiyadan keyin esa () qavslarning o`zi qo`yiladi.
Ko'rsatkichlar dasturchi ishini juda yengillashtiradi. Ko'rsatkichlar yordamida programma modullashadi, qismlarga bo`linadi. Bu esa keyinchalik dasturni rivojlantirishni osonlashtiradi. Dastur yozilish davrida xatolarni topishni yengillashtiradi. Bir misolda funksiyaning asosiy qismlarini ko`rib chiqaylik.
int foo(int k, int t) { int result; result = k * t; return (result);} Yuqoridagi foo funksiyamizning ismi, () qavslar ichidagi parametrlar – int tipidagi k va t lar kirish argumentlaridir, ular faqat ushbu funksiya ichida ko`rinadi va qo`llaniladi. Bunday o`zgaruvchilar lokal(local-mahalliy) deyiladi. result foo() ning ichida e`lon qilinganligi uchun u ham lokaldir. Demak, biz funksiya ichida o`zgaruvchilarni va sinflarni (class) e`lon qilishimiz mumkin ekan. Lekin funksiya ichida boshqa funksiyani e`lon qilib bo`lmaydi. foo() funksiyamiz qiymat ham qaytaradi. Qaytish qiymatining tipi foo() ning e`lonida eng boshida kelgan - int tipiga ega. Biz funksiyadan qaytarmoqchi bo`lgan qiymatning tipi ham funksiya e`lon qilgan qaytish qiymati tipiga mos kelishi kerak - ayni o`sha tipda bo`lishi yoki o`sha tipga keltirilishi mumkin bo`lgan tipga ega bo`lishi shart. Funksiyadan qiymatni return ifodasi bilan qaytaramiz. Agar funksiya hech narsa qaytarmasa e`londa void tipini yozamiz. Yani:
void funk(){ int g = 10; cout << g; return;} Bu funksiya void (bo`sh, hech narsasiz) tipidagi qiymatni qaytaradi. Boshqacha qilib aytganda, qaytargan qiymati bo`sh to`plamdir. Lekin funksiya hech narsa qaytarmaydi deya olmaymiz. Chunki hech narsa qaytarmaydigan maxsus ko'rsatkichlar ham bor. Ularning qaytish qiymati belgilanadigan joyga hech narsa yozilmaydi. Biz unday ko'rsatkichlarni keyinroq ko`rib chiqamiz. Bu yerda bir nuqta shuki, agar funksiya maxsus bo`lmasa, Lekin oldida qaytish qiymati tipi ko`rsatilmagan bo`lsa, qaytish qiymati int tipiga ega deb qabul qilinadi.
Void qaytish tipli ko'rsatkichlardan chiqish uchun return; deb yozsak yetarlidir.Yoki returnni qoldirib ketsak ham bo`ladi. Funksiyaning qismlari bajaradigan vazifasiga ko`ra turlicha nomlanadi. Yuqorida ko`rib chiqqanimiz funksiya aniqlanishi (function definition) deyiladi, chunki biz bunda funksiyaning bajaradigan amallarini funksiya nomidan keyin,{} qavslar ichida aniqlab yozib chiqyapmiz. Funksiya aniqlanishida {} qavslardan oldin nuqta-vergul [;] qo`yish xatodir. Bundan tashqari funksiya e`loni, prototipi yoki deklaratsiyasi (function prototype) tushunchasi qo`llaniladi. Bunda funksiyaning nomidan keyin hamon nuqta-vergul qo`yiladi, funksiya tanasi esa berilmaydi. C++ da funksiya qo`llanilishidan oldin uning aniqlanishi yoki hech bo`lmaganda e`loni kompilyatorga uchragan bo`lishi kerak. Agar funksiya e`loni boshqa ko'rsatkichlar aniqlanishidan tashqarida berilgan bo`lsa, uning kuchi ushbu fayl oxirigacha boradi. Biror bir funksiya ichida berilgan bo`lsa, kuchi faqat o`sha funksiya ichida tarqaladi. E`lon fayllarda aynan shu funksiya e`lonlari berilgan bo`ladi. Funksiya e`loni va funksiya aniqlanishi bir-biriga mos tushishi kerak. Masalan,
double square(char, bool); float average(int a, int b, int c); Funksiya e`lonlarda kirish parametrlarining faqat tipini yozish kifoya, xuddi square() funksiyasidek. Yoki kiruvchi parametrlarning nomi ham berilishi mumkin, bu nomlar kompilyator tarafidan e`tiborga olinmaydi, biroq dasturning o`qilishini ancha osonlashtiradi. Bulardan tashqari C++ da funksiya imzosi (function signature) tushunchasi bor. Funksiya imzosiga funksiya nomi, kiruvchi parametrlar tipi, soni, ketma-ketligi kiradi. Funksiyadan qaytuvchi qiymat tipi imzoga kirmaydi.
int foo(); //1
int foo(char, int); //2
double foo(); //3 - 1 funksiya bilan imzolari ayni.
void foo(int, char); //4 - 2 bilan imzolari farqli.
char foo(char, int); //5 - 2 bilan imzolari ayni.
int foo(void); //6 - 1 va 3 bilan imzolari ayni.
Yuqoridagi misolda kirish parametrlari bo`lmasa, biz () qavsning ichiga void deb yozishimiz mumkin (6 ga qarang). Yoki () qavslarning quruq o`zini yozaversak ham bo`ladi (1 ga qarang). Yana bir tushuncha - funksiya chaqirig`idir. Dasturda funksiyani chaqirib, qo`llashimiz uchun uning chaqiriq ko`rinishini ishlatamiz. () qavslari funksiya chaqirig`ida qo`llaniladi. Agar funksiyaning kirish argumentlari bo`lmasa, () qavslar bo`sh holda qo`llaniladi. Aslida () qavslar C++ da operatorlardir. Funksiya kirish parametrlarini har birini ayri-ayri yozish kerak, masalan, float average(int a, int b, int c); funksiyasini float average(int a,b,c); deb yozishimiz xatodir.
Hali aytib o`tganimizdek, funksiya kirish parametrlari ushbu funksiyaning lokal o`zgaruvchilaridir. Bu o`zgaruvchilarni funksiya tanasida boshqattan e`lon qilish sintaksis xatoga olib keladi.
27-listing.
Output:
# include int foo(int a, int b); //Funksiya prototipi,
//argumentlar ismi shart emas.
int main()
{ for (int k = 1; k<6; k++){
for (int l = 5; l>0; l--){
cout << foo(k,l) << " "; //Funksiya chaqirig`i.
}//end for (l...)
cout << endl;
}//end for (k...)
return (0);
} //end main()
//foo() funksiyasining aniqlanishi
int foo(int c, int d)
{ //Funksiya tanasi
return(c * d); }
Bizdaikkisiklichidafoo()funksiyamizchaqirilmoqda. Funksiyaga k va l o`zgaruvchilarining nusxalari uzatilmoqda. Nusxalarning qiymati mos ravishda funksiyaning aniqlanishida berilgan c va d o`zgaruvchilarga berilmoqda. k va l ning nusxalari deganimizda adashmadik, chunki ushbu o`zgaruvchilarining qiymatlari funksiya chaqirig`idan hech qanday ta`sir ko`rmaydi. C++ dagi ko'rsatkichlarning bir noqulay tarafi shundaki, funksiyadan faqat bitta qiymat qaytadi. Undan tashqari yuqorida ko`rganimizdek, funksiyaga berilgan o`zgaruvchilarning faqat nusxalari bilan ish ko`rilarkan. Ularning qiymatini normal sharoitda funksiya ichida o`zgartirish mumkin emas. Lekin bu muammolar ko`rsatkichlar yordamida osonlikcha hal etiladi. Funksiya chaqiriqlarida avtomatik ma`lumot tipining konversiyasi bajariladi. Bu amal kompilyator tomonidan bajarilganligi sababli ko'rsatkichlarni chaqirganda ehtiyot bo`lish kerak. Javob xato ham bo`lishi mumkin. Shu sababli kirish parametrlar tipi sifatida katta hajmli tiplarni qo`llash maqsadga muvofiq bo`ladi. Masalan, double tipi har qanday sonli tipdagi qiymatni o`z ichiga olishi mumkin. Lekin bunday qiladigan bo`lsak, biz tezlikdan yutqazishimiz turgan gap. Avtomatik konversiyaga misol keltiraylik.
28-listing.
Output:
int division(int m, int k) {
return (m / k);
}
dasturda chaqirsak:...
float f = 14.7;
double d = 3.6;
int j = division(f,d); //f 14 bo`lib kiradi, d 3 bo`lib kiradi
// 14/3 - butun sonli bo`lish esa 4 javobini beradi
cout << j;
4
Demak, kompilyator f va d o`zgaruvchilarining kasr qismlarini tashlab yuborar ekan. Qiymatlarni pastroq sig`imli tiplarga o`zgartirish xatoga olib keladi.