C++ nom maydonlari (Namespaces)
C++ tilida nomlar maydoni - bu identifikatorlarni boshqa nomlar yoki global nomlar maydonidagi o'xshash identifikatorlardan ajratishga yordam beradigan tegishli nomlar yoki identifikatorlar (funksiyalar, sinflar, o'zgaruvchilar) to'plami.
Keling, quyidagi kodni ko'rib chiqaylik:
int main() {
int var;
// Xato: ziddiyatli deklaratsiya
double var;
}
Bu kod muvaffaqiyatli kompilyatsiya qilinmadi, chunki biz main() funksiyasi doirasida bir xil nomli var ikkita o‘zgaruvchini e’lon qildik. C++ da bu nomlash konflikti sifatida tanilgan.
Kodni xatosiz kompilyatsiya qilishning bir usuli - bitta varni boshqa vardan alohida boshqa doirada e'lon qilish:
int main() {
int var;
// alohida mahalliy doira
{
double var;
}
return 0;
}
Biroq, agar nomlar maydonidan foydalansak, bir xil doiradagi bir nechta o'zgaruvchilar (yoki massivlar, vektorlar, funksiyalar va boshqalar) uchun bir xil nomdan foydalanishimiz mumkin.
Boshqacha qilib aytadigan bo'lsak, C++ tilidagi nomlar bo'shliqlari, ayniqsa, agar u katta loyiha bo'lsa, dastur ichidagi nomlash ziddiyatlarini oldini olishning bir usuli hisoblanadi.
Biz namespace kalit so'zidan foydalanish va uning doirasidagi ob'ektlarimizni e'lon qilish/aniqlash orqali nom maydoni yaratishimiz mumkin:
namespace dbl {
double var;
}
Bu erda biz dbl nomli fazoni yaratdik va uning ichida var nomli double o'zgaruvchisini e'lon qildik.
Keyin dbl ga tegishli o'zgaruvchidan foydalanayotganimizni aniqlash uchun :: ko'lamini aniqlash operatorini nomlar maydonidan tashqarida ishlatishimiz mumkin.
dbl::var;
Buni oddiy misol bilan aniqlab olaylik.
#include <iostream>
using namespace std;
// qo'sh o'zgaruvchiga ega nom maydoni yarating
namespace dbl {
double var;
}
int main() {
// bir xil nomdagi int o'zgaruvchini yarating
int var = 5;
// nomlash ziddiyatini oldini olish uchun yaratilgan nom maydonidan foydalaning
dbl::var = 5.55;
// o'zgaruvchilarni ko'rsatish
cout << "dbl var = " << dbl::var << endl;
cout << "main var = " << var << endl;
return 0;
}
Chiqish
dbl var = 5.55
main var = 5
Bu erda biz dbl nom maydonini yaratdik, bu erda double o'zgaruvchisi var.
Keyin main() da int turidagi boshqa varni aniqladik. dbl::var kodidan foydalanib, biz main() o'zgaruvchisi bilan birga dbl nom maydonining o'zgaruvchisidan foydalanishimiz mumkin.
Shunday qilib, biz dbl nom maydonidan foydalanish dasturimizda nomlash ziddiyatining paydo bo'lishiga to'sqinlik qilganini ko'rishimiz mumkin.
Bitta dasturda bir nechta nom maydonidan foydalanishimiz mumkin. Bir nechta nomlar maydoni, ayniqsa, ko'p qatorli kodli katta dasturlarni yozishda foydalidir.
Xuddi shunday, nom bo'shliqlari boshqa nomlar maydoniga joylashtirilishi mumkin, bu bizga kodimizni mantiqiy va ierarxik tarzda tuzishga imkon beradi.
Bir nechta va ichma-ich nom maydonlari (nested namespaces)ga misol quyida keltirilgan:
#include <iostream>
using namespace std;
namespace one {
void display() {
cout << "namespace one" << endl;
}
// bir nom maydoni ichida boshqa nom maydoni yarating
namespace one_one {
void display() {
cout << "namespace one_one" << endl;
}
}
}
namespace two {
void display() {
cout << "namespace two" << endl;
}
}
void display() {
cout << "not a namespace" << endl;
}
int main() {
// nomlar maydonining display() funksiyasini chaqiring
one::display();
// one_one nom maydonining display() funksiyasini chaqiring
one::one_one::display();
// nomlar maydonining display() funksiyasini chaqiring
two::display();
// nom maydonidan tashqarida display() funksiyasini chaqiring
display();
return 0;
}
Chiqish
namespace one
namespace one_one
namespace two
not a namespace
Bu erda biz ikkita one va two nom maydonini yaratdik, ularning har biri o'z display() funksiyalariga ega.
Shuningdek, biz one nom maydoniga joylashtirilgan one_one nom maydonida boshqa display() funksiyasini aniqladik.
Keyin biz barcha nom maydonlaridan tashqarida display() funksiyasini e'lon qildik.
Keyin :: operatori yordamida har bir nom maydonining display() funksiyalarini chaqirdik.
one_one ichki nom maydoni ichida display() funksiyasini qanday chaqirganimizga e'tibor bering.
one::one_one::display();
Ko'rib turganingizdek, ichki ichki nomlar maydoni a'zolariga kirish uchun one tashqi nom maydonini va one_one ichki nom maydonini o'z ichiga olishimiz kerak.
Nihoyat, :: operatorisiz nom maydoni ichida aniqlanmagan funksiya chaqiriladi.
// nom maydonidan tashqarida display() funksiyasini chaqiring
display();
using direktivasi yordamida :: operatoridan foydalanishni chetlab o'tishimiz mumkin.
Aslida, biz using namespace std; kodi bilan shu paytgacha ko'pgina dasturlarimiz uchun ushbu direktivadan foydalanganmiz.
std nom maydonidan foydalanmasdan, ba'zi muhim IO ob'ektlarimiz va standart istisnolar quyidagilarga aylanishini aytib o'tgan edik:
// std nom maydonidan foydalanmasdan
std::cout
std::cin
std::endl
std::exception
std::bad_cast
using namespace std; kodini dasturimizga qo'shish orqali biz std nom maydonida aniqlangan identifikatorlar uchun std:: qismini o'tkazib yuborishimiz mumkin:
// std nom maydonidan foydalanish
cout
cin
endl
exception
bad_cast
Bu boshqa nomlar uchun ham amal qiladi.
Biz bitta dasturda bir nechta nom maydoni yaratishimiz va foydalanishimiz mumkin.
#include <iostream>
using namespace std;
namespace one {
void display() {
cout << "namespace one" << endl;
}
}
namespace two {
void display() {
cout << "namespace two" << endl;
}
}
int main() {
using namespace one;
// biriga qo'ng'iroq qiling::display()
display();
// ikkinchi qo'ng'iroq:: displey
two::display();
return 0;
}
Chiqish
namespace one
namespace two
Bu erda using namespace one; kodini dasturimizga qo'shish orqali biz uning display() funksiyasini :: rezolyutsiyasi operatoridan foydalanmasdan chaqirishimiz mumkin.
two nom maydonining display() funksiyasini chaqirmoqchi bo'lsak, :: operatori talab qilinadi.
[!NOTE] Biz
using namespace one;direktivamiznimain()ichiga kiritdik,using namespace std;kodi esamain()dan tashqarida (va boshqa barcha mumkin bo'lgan doiralar).#include <iostream> using namespace std; ... .. ... ... .. ... int main() { using namespace one; ... .. ... }Bu shuni anglatadiki, bizonenom maydonidanmain()funksiyasi doirasida foydalanganmiz,stdnom maydoni esa global miqyosda ishlatilmoqda. Nomlar maydonidan foydalanish global bo'lishi yoki ma'lum bir doira bilan cheklangan bo'lishi mumkin. Bu nomlar maydonidan qanday foydalanmoqchi ekanligimizga bog'liq.
Biz hozir ko'rgan misol dasturidan ko'rinib turibdiki, using direktivasi bizning dasturimizda ko'plab nomlash ziddiyatlarini keltirib chiqarishi mumkin. Masalan,
using namespace one;
namespace one {
void display() {...}
}
void display() {...}
int main() {
// Xato: displey() ning bir nechta nusxasi
display();
return 0;
}
Yuqoridagi kodda biz global miqyosda one nom maydonidan foydalandik.
Shuning uchun, biz display() funksiyasini chaqirganimizda, kompilyator one nom maydoniga tegishli funksiyani chaqirayotganimizni yoki funksiyani nom maydonidan tashqarida chaqirayotganimizni aniqlay olmaydi.
Bir xil identifikatorga ega bo'lgan bir nechta nomlar maydonidan foydalanganda nomlash ziddiyatlari ham paydo bo'lishi mumkin. Masalan,
namespace one {
void display() {...}
}
namespace two {
void display() {...}
}
using namespace one;
using namespace two;
int main() {
// Xato: displey() ning bir nechta nusxasi
display();
return 0;
}
C++ standart kutubxonasidagi barcha fayllar std nom maydonidagi barcha ob'ektlarni e'lon qiladi.
Biroq, C++ standartidagi ushbu ob'ektlarning ko'pchiligi (funksiyalar, ob'ektlar va boshqalar) identifikatorlarni boshqa kutubxonalardagi boshqa nomlar bilan bo'lishishi mumkin.
Yoki ular identifikatorlarni dasturimizda qo'lda yaratganlar bilan baham ko'rishlari mumkin.
Shuning uchun using namespace std; kodini global miqyosga kiritish yomon amaliyot hisoblanadi.
Masalan,
#include <iostream>
using namespace std;
string cout() {
return "\n";
}
int main() {
// Xato: cout noaniq
cout << "Testing!" << endl;
return 0;
}
Bu erda biz std nom maydonidan foydalangan holda o'zimizning cout() funksiyamizni aniqladik. Bu bizning dasturimizda nomlash ziddiyatiga olib keladi va biz cout dan foydalanganda xatoga yo'l qo'yamiz.