-dastur - Tiplarni dinamik tarzda aniqlash. Reja: Tiplarni dinamik tarzda aniqlash

1.11-dastur. Reinterpret_cast operatoridan foydalanish.
#include "stdafx.h" #include
using namespace std;
int main(){
int i;
char *p = "Salom Buxoro";
i = reinterpret_cast (p); // koʻrsatkichni int tipiga almashtirish. cout << i}
|
Bu yerda reinterpret_cast operatori p ko‘rsatkichni butun son int qiymatiga aylantiradi. Bu o‘zgartirish asosiy tipni o‘zgarish ifodalaydi. Bu tip almashtirishni oddiy kasting bilan ham amalga oshirish mumkin.
Yangi to‘rtta cast operatorlari bilan anʻanaviy tipni almashtirish amali (kasting) qanday farq mavjud. Baʻzi dasturchilar to‘rt cast operatorlari yuqorida tasvirlangan butunlay anʻanaviy tipni almashtirish amalining o‘rniga deb o‘ylashlari mumkin. Ularning o‘rniga har doim odatiy tipni almashtirishning yangi vositalaridan foydalanish kerakmi? – degan savol qiziqtirishi mumkin. Aslida barcha dasturchilar uchun umumiy qoida yo‘q. Yangi operatorlar bir maʻlumot tipini boshqasiga almashtirishni ancha xavfli amallar xavfsizligini yaxshilash uchun yaratilganligi sababli, ko‘pgina C++ dasturchilari faqat shu maqsadda ishlatilishi kerakligiga ishonch hosil qilgan. Bunga eʻtiroz qilib bo‘lmaydi. Boshqa dasturchilar anʻanaviy tipi almashtirish amali ko‘p yillar davomida ularga "sadoqat" bilan xizmat qilib bergan, undan shunday voz kechish oson emas. Masalan, oddiy va nisbatan xavfsiz tipdagi kasting amallarini bajarish uchun read() va write() funksiyalarini chaqirganda talab qilinadi.
Ammo shunday dasturlashda jarayonlar borki, ularda cast operatorlaridan foydalanish maqsadga muvofiqdir, masalan, polimorf tiplar uchun cast amallarini bajarishda albatta dynamic_cast operatoridan foydalanishga to‘g‘ri keladi.
Yangi nomlar fazosi yaratish. Nomlar fazosi haqida qisqacha maʻlumotga ega bo‘lsangiz kerak. Endi bu tushunchani batafsilroq ko‘rib chiqamiz va foydalanuvchining nomlar fazosi qanday yaratilishini ko‘rib chiqamiz. Namespaces tushunchasi C++ dasturlash tilida nisbatan yaqinda paydo bo‘lgan tushunchalarda biridir. Ular nomlarning o‘zaro bir xilligini oldini olish (kolliziya saqlanish) uchun identifikatorlarning nomlarini lokalizatsiya qilish uchun mo‘ljallangan. C++ dasturlash muhitida, o‘zgaruvchan nomlari aql bovar qilmaydigan bir qator operatorlari, funksiyalari, metodlari va sinflar mavjud. Nomlar fazosi tushunchasi kiritilishidan oldin, bu nomlarning barchasi bir xil global nomlar bo‘lib, ko‘plab nizolarni (xatoliklarni, o‘zaro bir xilliklarni) keltirib chiqargan. Agar dasturda dasturchi o‘zining toupper() funksiyasini yaratgan bo‘lsa, masalan, agar, u (uning parametrlari ro‘yxatiga qarab) standart kutubxona toupper(), funksiyasini almashtirish mumkin. Ammo, ikkala fnuksiyalarning nomlari bir xil global namespaceda saqlangan bo‘ladi. Bir xil dasturlarni turli ishlab chiqaruvchilarning funksiyalari va sinflar, kutubxonalar foydalanganda nomlar bir xil bo‘lgan funksiyalar dastur bajarilishida ziddiyatlarga duch keladi. Bunday holda, bir kutubxonada aniqlangan nomlar boshqa kutubxonada aniqlangan nomlar bilan to‘qnashishi mumkin.
Barcha muammolar nomlar fazosi tushunchasi va namespace kalit so‘z tomonidan hal qilindi. Bu kalit so‘z yangi nomlar fazosi eʻlon qilish orqali barcha funksiya, sinf va kutubxonalarni mahalliylashtirish imkonini beradi. Bu nomlar fazosi turli kontekstlarda bir xil nomdagi funksiya va sinflar ziddiyatga olib kelmasdan ishlatish imkonini beradi. std standart kutubxona (std -Standart Library) juda ko‘p foydalanilgan bo‘lishi mumkin. Tilning dastlabki variantlarida C++ kutubxonasi global nomlar fazosida belgilangan. Sezilarli nomlar ziddiyatlari ehtimolini kamaytirish uchun std namespace nomlar fazosi yaratilgan. Bundan tashqari, ziddiyatlarni oldini olish uchun nomlar fazrsi ko‘lamini mahalliylashtirish uchun dasturda o‘z nom fazongizni yaratishingiz mumkin. Bu shaxsiy sinf yoki funksiya kutubxonalar yaratishda, ayniqsa, muhim ahamiyatga ega.
Namespace kalit so‘zi nomlar fazosida sohalarni qo‘shib global namespacedan ajratish imkonini beradi. Aslida, bir nomlar fazosi doirasi belgilaydi. Namespace kalit so‘zidan foydalanishning asosiy shakli quyida ko‘rsatilgan:
namespace Space{
//....
}
|
Namespace kontentida aniqlangan ixtiyoriy har bir element, bu namespace doirasi bajariladi.
1.12-dastur. myNameSpace nomli namespace eʻlon namuna.
namespace MyNameSpace { int i,k;
void myfunc (int j) { cout <<ј; } class myclass {
public:
void set(int x) { i = x; } int get() { return i; }
};
};
|
Mynamespace nomli namespaceda i va k o‘zgaruvchilarning nomlari, myfunc() funksiyasi va myclass sinfi yaratilgan.
Bu namespace ichida o‘zgaruvchilarga, funksiyava sinflarga to‘g‘ridan- to‘g‘ri murojaat qilish va yangisini kirish mumkin. Biroq, namespace kalit so‘z
maʻlum bir dastur sohasini belgilaydi, chunki, namespace tashqaridan namespace ichida eʻlon obʻyektlarni foydalanish imkoniyatini faqat ziddiyatlarni oldini olgan holda foydalanishga ruxsat beradi. Masalan, Mynamespace nomli namespace tarkibiga kirmagan dasturning bir qismida i o‘zgaruvchiga 28 qiymat berish uchun quyidagicha ifoda yozish lozim:
Mynamespace nomli namespace sohasiga kiradigan Myclass tipidagi obʻyektni odatdagidek eʻlon qilishimiz mumkin, ammo kirmagan Myclass tipidagi obʻyektni quyidagicha eʻlon qilish lozim:
Shunday qilib, bu soha tashqarida nomlar fazosining nomi bilan kengaytirish operatori (::) bilan murojaat qilish mumkin.
Agar dasturda namespace aʻzolariga tez-tez murojaat bo‘ladigan bo‘lsa, har doim uning nomi va kengaytirish operatori yozish dasturning ko‘rinishiga va dasturchining ko‘p yozishiga sabab bo‘lishi mumkin. Ushbu muammoni hal qilish uchun foydalanuvchining nomlar fazosidan foydalanish fragmenti ishlab chiqilgan. Ushbu fragment ikkita asosiy shaklga ega:
using namespace MyNameSpace;
using MyNameSpace::myclass;
|
Fragmentning birinchi qatorida keltirilgan shaklda aʻzolar kirmoqchi bo‘lgan nomlar fazosining nomini bildiradi. Foydalanish fragmentida bu shakl yordamida yozilgan nomlar fazosidagi barcha aʻzolarga joriy nom mavjud deb olinadi va to‘g‘ridan-to‘g‘ri murojaat qilish mumkin, fazo nomi va kengaytirish operatori har safar yozish kerak bo‘lmaydi.
Fragmentning ikkinchi qatorida using fragmentining ikkinchi shaklidan foydalanilganda faqat fragmentda ko‘rsatilgan nomdosh aʻzosi ko‘rinadi. Masalan, yuqorida taʻriflangan mynamespace nomli namespace bo‘lsa, unda quyidagi fragmentlar va operatorlaridan foydalanish to‘g‘ri:
using MyNameSpace::k;
int main(){
|
k = 10;
return 0;
}
|
|
Fragment asosida faqat k o‘zgaruvchi ko‘rinadi. Demak, ko‘rsatma to‘g‘ri.
using namespace MyNameSpace;
int main(){
k = 10;
i = 10;
myclass ob; myfunc(25);
return 0;
}
|
Bu fragmentda MyNameSpace fazoning barcha aʻzolari ko‘rinadi va Demak, ko‘rsatma to‘g‘ri.
Shuningdek, bir xil nom bilan bir nechta fazoni eʻlon qilish imkoniyati mavjud. Bu bir necha fayllar ichiga fazolarni ajratish imkonini beradi, yoki hatto bitta fayl ichida fazolarni ajratish imkonini. Buni dasturchining dasturga ishlatayotgan funksiya va sinflarini ajratish uchun qo‘llash mumkin. Quyidagi misolni ko‘rib chiqamiz:
namespace NS { int a;} namespace NS { int b;}
int main(){
NS::a = 10;
NS::b = 10;
return 0;
}
|
Bu yerda NS namespace ikki qismga bo‘lingan. Shunga qaramay, har bir qismi aʻzolariga ham bir xil nom bilan murojaat qilingan, yaʻni NS fazo.
Bir dastur faylida bir nomli fazolarni eʻlon qilish maqsadga muvofiq. Ammo, dasturda 2 va undan ortiq dastur fayllari ishlatilsa va fazolarni birlashtirish kerak bo‘lsa, shunda fazoni ikkiga ajratish usulidan foydalanish mumkin. Boshqa so‘zlar bilan aytganda, bir namespace faqat boshqa namespace bo‘lishi mumkin emas. Bu bir nomli fazo eʻlon qilinishi mumkin emas degan maʻnoni anglatadi, masalan, bir funksiya ichida.
Nomsiz fazo - fazoning maxsus turidir. Muayyan fayl ichida noyob identifikatorlar, aʻzolarni yaratish imkonini beradi. Nomsiz fazoning asosiy shakli quyidagicha bo‘ladi:
namespace {
const float f = 3.1415;
int myIf(int a, int b){return a>b?1:0;}
}
|
Fazolarni nomsiz nomlashlar faqat bitta fayl doirasida maʻlum bo‘lgan noyob identifikatorlar va funksiya, sinflarni o‘rnatish imkonini beradi. Shu tarzda, nomaʻlum nomli fazoni o‘z ichiga olgan fayl ichida, ushbu fazoning aʻzolariga to‘g‘ridan-to‘g‘ri, hech qanday tushuntirishga muhtoj bo‘lmasdan foydalanish mumkin. Ammo bu fayldan tashqarida bunday identifikatorlar nomaʻlum bo‘lib qoladi.
Siz kichik va o‘rta dasturlar uchun o‘z fazolaringizni yaratishingiz shart emas. Ammo, agar qayta foydalanish uchun mo‘ljallangan fnuksiyalar, ssinflar va/yoki sinflar kutubxonalar yaratish uchun baʻzi namespace ichida sizning kodlaringiz joylashtirish yozgan dasturingizni tushunarliroq qiladi.
1.13-dasturda nomlar fazosini yaratish va foydalanishga misol keltirilgan. 1.13-dastur. Nomlar fazosini yaratish va foydalanish.
#include "stdafx.h" #include using namespace std;
// birinchi nomlar fazosi namespace NS1{
class demo{
int i; public:
demo(int x) { i=x; } void set(int x) { i=x; } int get() { return i; }
};
char str[] = "Nomlar fazolarini oʻrnatish\n"; int counter;
}
// ikkinchi nomlar fazosi namespace NS2 { int x, u; }
|
int main(){
NS1::demo ob(10);
cout <<" ob = " << ob.get() << endl; ob.set(99);
cout << "Endi ob = "<< ob.get() << endl; using NS1::str; cout << str;
using namespace NS1;
for(counter = 1; counter<=10; counter++) cout << counter << ends;
cout << endl;
NS2::x = 10; NS2::u = 20;
cout << "oʻzgaruvchilar x, y = "<< NS2::x<<", " << NS2::u << endl;
using namespace NS2; demo x_ob(x), y_ob(u);
cout << "x_ob, y_ob obʻyektlarning qiymati: "<< x_ob.get(); cout<< " " << y_ob.get() << endl;
system("pause"); return 0;
}
|
1.13 – dastur natijasi. Output
|
ob = 10
Endi ob = 99
Nomlar fazolarini oʻrnatish 1 2 3 4 5 6 7 8 9 10
oʻzgaruvchilar x, y = 10, 20
x_ob, y_ob obʻyektlarning qiymati: 10 20
|
Dasturda bir muhim nuqta ko‘rsatilgan: bir nechta nomlarni birgalikda ishlatganda, bir nomni boshqasi almashtirmaydi. Hozirgi fragmentda baʻzi nomlarni kiritganingizda, uning nomlari o‘sha paytda boshqa nomdagi nomlarning o‘zida bo‘lishidan qatʻi nazar, bu fragmentda qo‘shiladi. Shunday qilib, dastur bajarilayotgan vaqtda, std, NS1 va NS2 namespaces global namespace bo‘lib qo‘shildi.
Yuqorida aytib o‘tilganidek, fayllar orasida yoki bitta fayl ichida nomlar fazosini ajratish mumkin, keyin esa bu nomlar fazosining aʻzolarini mazmunan birlashtiriladi. Birgalikda nomlar fazosini birlashtirish misolini ko‘rib chiqamiz.
1.14-dastur. Namespace larni birlashtirish.
#include "stdafx.h" #include using namespace std; namespace Demo {
int a; // Demo fazoda a oʻzgaruvchi
}
int x; // Global fazoda x oʻzgaruvchi namespace Demo {
int b; // Demo fazoda b oʻzgaruvchi
}
using namespace Demo; int main(){
a = b = x = 101;
cout << a << " " << a << " " << x << endl; system("pause");
return 0;
}
|
1.14 – dastur natijasi. Output
|
101 101 101
|
Bu misolda a va b o‘zgaruvchilari ham bir xil fazoda - Demo fazoda va x o‘zgaruvchisi global fazoda eʻlon qilingan. Ammo, using namespace Demo; fragmenti bilan, a va b o‘zgaruvchilarini va global o‘zgaruvchini ishlatish mumkin.
Yuqorida aytib o‘tilganidek, C++ standart o‘z std fazosiga ega va butun kutubxonalarini qamrab oladi. Shuning uchun ushbu kitobdagi barcha dasturlar quyidagi ko‘rsatmaga ega:
Bu fragment std namespace joriy qiladi va bevosita C++ standart kutubxonasida belgilangan funksiyalari va sinflar nomlarini kirish imkonini beradi. std namespace kengaytirish operatori yordamida har doim ishlatish maqsadga muvofiq emas.
Biroq, agar xohlasangiz, har bir identifikatordan oldin std namespace nomini va kengaytirish operatorini qo‘yishingiz mumkin, xato bo‘lmaydi. Masalan, quyidagi 1.15-dasturda standart std kutubxonasi global doirada kiritilmagan.
1.15-dastur. Ochiq foydalanish uchun nom ko‘rsatilgan.
#include "stdafx.h" #include int main(){
double val;
std::cout << "Son kiriting:"; std::cin >> val;
std::cout << "Son:"; std::cout << val << std::endl;
system("pause"); return 0;
}
|
Ushbu dasturda ko‘rsatilganidek, standart kirish va chiqish oqimlari cin va cout operatorlaridan foydalanish uchun nomlaridan oldin ularning qaysi fazodanligini belgilash kerak.
Agar dasturingiz C++ standart kutubxonasidan keng foydalanish zarur bo‘lmasa, std namespaceni global doirada kiritish shart emas. Ammo, agar sizning dasturingiz standart kutubxona aʻzolari uchun minglab murojaatlarni o‘z ichiga olsa, har aʻzo uchun std fazosini chaqirish juda ham oson kechmaydi. Shuning uchun uni global kiritish nisbatan dasturchi oson va std barcha standart identifikatorlarni o‘z ichiga oladi.
Agar dasturingizda standart kutubxonadan faqat bir necha aʻzodan foydalansangiz, bu bir necha aʻzolarni alohida belgilash uchun using fragmentidan foydalanish qulayroq bo‘lishi mumkin. Ushbu yondashuvning afzalligi shundaki, siz dasturda ko‘rsatilgan aʻzolarni fazosini ko‘rsatmasdan kiritishingiz mumkin va ayni paytda butun standart kutubxonani global nomda kiritishingiz shart emas. Masalan:
#include "stdafx.h" #include
using std::cin; using std::cout;
|
int main(){
double val;
cout << "Son kiriting:"; cin >> val;
cout << "Son:";
cout << val << std::endl; system("pause");
return 0;
}
|
Bu yerda standart kirish va chiqish oqimlari cin va cout operatorlari to‘g‘ridan-to‘g‘ri ishlatilishi mumkin, ammo ayni paytda std fazoning boshqa aʻzolar joriy soxadan tashqarida qoldiriladi. Maslan, endl;
Avvalroq, C - tilining kutubxonasi global nomda belgilanganligi haqida aytib o‘tilgan edi. Agar eski C dasturini yangilashingiz kerak bo‘lsa, siz ham unda foydalanish uchun namespace std foydalanish kerak bo‘ladi, yoki kengaytirish operatori va std namespace nomini kiritish lozim. Agar yangi uslub sarlavhalari bilan eski header fayllarni o‘rniga, ayniqsa, muhim ahamiyatga ega (*.h). Eski header fayllar global namespace o‘z mazmunini joylashtiradi va yangi uslub sarlavhalari uchun std namespace o‘z mazmunini joylashtiradi.
Agar, nomlar fazosini global sifatida bitta faylga eʻlon qilib, cheklamoqchi bo‘lsangiz, S tilida statik deb eʻlon qilish kerak edi, yaʻni, statik identifikator bilan. Masalan, quyidagi ikki fayl bir xil dasturning bir qismi deb faraz qilaylik:
Birinchi fayl
|
Ikkinchi fayl
|
static int counter; void f1(){
counter == 99; // toʻgʻri
}
|
extern int counter; void f2() {
counter = 10; // Xato
}
|
counter o‘zgaruvchisi birinchi faylda aniqlanganligi uchun ham uni birinchi faylda ishlatishingiz mumkin. Ikkinchi faylda counter o‘zgaruvchisi bilan fragmentdagi extern identifikatorini ko‘rsatishiga qaramay, bu o‘zgaruvchini ishlatishga urinish xatolikka olib keladi. Birinchi fayldagi counter o‘zgaruvchisini statik deb eʻlon qilib, uning ko‘lamini shu faylga cheklangan.
Statik identifikator bilan global o‘zgaruvchilar deklaratsiyalari C++ hali ham mavjud bo‘lsa-da, bu qochish maqsadda eng yaxshi yo‘l Nomlar fazosidan foydalanish hisoblanadi, quyidagi misolda ko‘rsatilgandek.
Birinchi fayl
|
Ikkinchi fayl
|
namespace { int counter;
}
void f1(){
counter == 99; // toʻgʻri
}
|
extern int counter; void f2() {
counter = 10; // toʻgʻri
}
|
|