Zamonaviy C++ aqlli ko'rsatkichlarini (unique_ptr, shared_ptr, weak_ptr) mustahkam xotira boshqaruvi uchun o'rganing, xotira sizib chiqishining oldini oling va ilova barqarorligini oshiring. Eng yaxshi amaliyotlar va amaliy misollarni bilib oling.
C++ ning Zamonaviy Xususiyatlari: Samarali Xotira Boshqaruvi uchun Aqlli Ko'rsatkichlarni O'zlashtirish
Zamonaviy C++ da aqlli ko'rsatkichlar xotirani xavfsiz va samarali boshqarish uchun ajralmas vositalardir. Ular xotirani bo'shatish jarayonini avtomatlashtirib, an'anaviy C++ dasturlashida keng tarqalgan xotira sizib chiqishi va osilib qolgan ko'rsatkichlarning oldini oladi. Ushbu keng qamrovli qo'llanma C++ da mavjud bo'lgan aqlli ko'rsatkichlarning turli xillarini o'rganadi va ulardan samarali foydalanish bo'yicha amaliy misollarni taqdim etadi.
Aqlli Ko'rsatkichlarga bo'lgan Ehtiyojni Tushunish
Aqlli ko'rsatkichlarning o'ziga xos xususiyatlariga sho'ng'ishdan oldin, ular hal qiladigan muammolarni tushunish juda muhim. Klassik C++ da dasturchilar new
va delete
yordamida xotirani qo'lda ajratish va bo'shatish uchun mas'uldirlar. Bu qo'lda boshqaruv xatolarga moyil bo'lib, quyidagilarga olib keladi:
- Xotira sizib chiqishi: Xotiradan foydalanib bo'lingach, uni bo'shatmaslik.
- Osilib qolgan ko'rsatkichlar: Allaqachon bo'shatilgan xotiraga ishora qiluvchi ko'rsatkichlar.
- Ikki marta bo'shatish: Bir xil xotira blokini ikki marta bo'shatishga urinish.
Bu muammolar dasturning ishdan chiqishiga, kutilmagan xatti-harakatlarga va xavfsizlik zaifliklariga olib kelishi mumkin. Aqlli ko'rsatkichlar dinamik ajratilgan obyektlarning yashash davrini avtomatik ravishda boshqarib, Resursni Olish - bu Initsializatsiya (RAII) tamoyiliga amal qilib, oqlangan yechim taklif qiladi.
RAII va Aqlli Ko'rsatkichlar: Kuchli Birikma
Aqlli ko'rsatkichlar ortidagi asosiy konsepsiya RAII bo'lib, u resurslarni obyekt yaratilishi paytida olinishi va obyekt yo'q qilinishi paytida bo'shatilishi kerakligini belgilaydi. Aqlli ko'rsatkichlar - bu oddiy ko'rsatkichni o'z ichiga olgan va aqlli ko'rsatkich amal qilish doirasidan chiqqanda ko'rsatilgan obyektni avtomatik ravishda o'chiradigan sinflardir. Bu hatto istisnolar mavjud bo'lganda ham xotiraning doimo bo'shatilishini ta'minlaydi.
C++ dagi Aqlli Ko'rsatkichlarning Turlari
C++ aqlli ko'rsatkichlarning uchta asosiy turini taqdim etadi, ularning har biri o'ziga xos xususiyatlarga va qo'llanilish holatlariga ega:
std::unique_ptr
std::shared_ptr
std::weak_ptr
std::unique_ptr
: Yagona Egalik
std::unique_ptr
dinamik ravishda ajratilgan obyektga yagona egalikni ifodalaydi. Istalgan vaqtda faqat bitta unique_ptr
ma'lum bir obyektga ishora qilishi mumkin. unique_ptr
amal qilish doirasidan chiqqanda, u boshqaradigan obyekt avtomatik ravishda o'chiriladi. Bu unique_ptr
ni obyektning yashash davri uchun yagona subyekt mas'ul bo'lishi kerak bo'lgan holatlar uchun ideal qiladi.
Misol: std::unique_ptr
dan foydalanish
#include <iostream>
#include <memory>
class MyClass {
public:
MyClass(int value) : value_(value) {
std::cout << "MyClass qiymat bilan yaratildi: " << value_ << std::endl;
}
~MyClass() {
std::cout << "MyClass qiymat bilan yo'q qilindi: " << value_ << std::endl;
}
int getValue() const { return value_; }
private:
int value_;
};
int main() {
std::unique_ptr<MyClass> ptr(new MyClass(10)); // unique_ptr yaratish
if (ptr) { // Ko'rsatkichning yaroqliligini tekshirish
std::cout << "Qiymat: " << ptr->getValue() << std::endl;
}
// ptr amal qilish doirasidan chiqqanda, MyClass obyekti avtomatik ravishda o'chiriladi
return 0;
}
std::unique_ptr
ning Asosiy Xususiyatlari:
- Nusxa olmaslik:
unique_ptr
nusxalanmaydi, bu bir nechta ko'rsatkichning bir xil obyektga egalik qilishining oldini oladi. Bu yagona egalikni ta'minlaydi. - Ko'chirish Semantikasi:
unique_ptr
std::move
yordamida ko'chirilishi mumkin, bu esa egalikni birunique_ptr
dan boshqasiga o'tkazadi. - Maxsus O'chiruvchilar: Siz
unique_ptr
amal qilish doirasidan chiqqanda chaqiriladigan maxsus o'chiruvchi funksiyani belgilashingiz mumkin, bu sizga dinamik ajratilgan xotiradan boshqa resurslarni (masalan, fayl identifikatorlari, tarmoq soketlari) boshqarish imkonini beradi.
Misol: std::unique_ptr
bilan std::move
dan foydalanish
#include <iostream>
#include <memory>
int main() {
std::unique_ptr<int> ptr1(new int(42));
std::unique_ptr<int> ptr2 = std::move(ptr1); // Egalikni ptr2 ga o'tkazish
if (ptr1) {
std::cout << "ptr1 hali ham yaroqli" << std::endl; // Bu bajarilmaydi
} else {
std::cout << "ptr1 endi null" << std::endl; // Bu bajariladi
}
if (ptr2) {
std::cout << "ptr2 tomonidan ko'rsatilgan qiymat: " << *ptr2 << std::endl; // Natija: ptr2 tomonidan ko'rsatilgan qiymat: 42
}
return 0;
}
Misol: std::unique_ptr
bilan Maxsus O'chiruvchilardan foydalanish
#include <iostream>
#include <memory>
// Fayl identifikatorlari uchun maxsus o'chiruvchi
struct FileDeleter {
void operator()(FILE* file) const {
if (file) {
fclose(file);
std::cout << "Fayl yopildi." << std::endl;
}
}
};
int main() {
// Faylni ochish
FILE* file = fopen("example.txt", "w");
if (!file) {
std::cerr << "Faylni ochishda xatolik." << std::endl;
return 1;
}
// Maxsus o'chiruvchi bilan unique_ptr yaratish
std::unique_ptr<FILE, FileDeleter> filePtr(file);
// Faylga yozish (ixtiyoriy)
fprintf(filePtr.get(), "Salom, dunyo!\n");
// filePtr amal qilish doirasidan chiqqanda, fayl avtomatik ravishda yopiladi
return 0;
}
std::shared_ptr
: Umumiy Egalik
std::shared_ptr
dinamik ravishda ajratilgan obyektga umumiy egalikni ta'minlaydi. Bir nechta shared_ptr
nusxalari bir xil obyektga ishora qilishi mumkin va obyekt faqat unga ishora qiluvchi oxirgi shared_ptr
amal qilish doirasidan chiqqanda o'chiriladi. Bunga havola sanash orqali erishiladi, bunda har bir shared_ptr
yaratilganda yoki nusxalanganda hisobni oshiradi va yo'q qilinganda hisobni kamaytiradi.
Misol: std::shared_ptr
dan foydalanish
#include <iostream>
#include <memory>
int main() {
std::shared_ptr<int> ptr1(new int(100));
std::cout << "Havolalar soni: " << ptr1.use_count() << std::endl; // Natija: Havolalar soni: 1
std::shared_ptr<int> ptr2 = ptr1; // shared_ptr nusxasini olish
std::cout << "Havolalar soni: " << ptr1.use_count() << std::endl; // Natija: Havolalar soni: 2
std::cout << "Havolalar soni: " << ptr2.use_count() << std::endl; // Natija: Havolalar soni: 2
{
std::shared_ptr<int> ptr3 = ptr1; // Amal qilish doirasi ichida shared_ptr nusxasini olish
std::cout << "Havolalar soni: " << ptr1.use_count() << std::endl; // Natija: Havolalar soni: 3
} // ptr3 amal qilish doirasidan chiqadi, havolalar soni kamayadi
std::cout << "Havolalar soni: " << ptr1.use_count() << std::endl; // Natija: Havolalar soni: 2
ptr1.reset(); // Egalikdan voz kechish
std::cout << "Havolalar soni: " << ptr2.use_count() << std::endl; // Natija: Havolalar soni: 1
ptr2.reset(); // Egalikdan voz kechish, obyekt endi o'chiriladi
return 0;
}
std::shared_ptr
ning Asosiy Xususiyatlari:
- Umumiy Egalik: Bir nechta
shared_ptr
nusxalari bir xil obyektga ishora qilishi mumkin. - Havola Sanash: Unga ishora qiluvchi
shared_ptr
nusxalari sonini kuzatib, obyektning yashash davrini boshqaradi. - Avtomatik O'chirish: Obyekt oxirgi
shared_ptr
amal qilish doirasidan chiqqanda avtomatik ravishda o'chiriladi. - Potoklar uchun Xavfsizlik: Havolalar sonini yangilash potoklar uchun xavfsizdir, bu
shared_ptr
ni ko'p potokli muhitlarda ishlatish imkonini beradi. Biroq, ko'rsatilgan obyektning o'ziga kirish potoklar uchun xavfsiz emas va tashqi sinxronizatsiyani talab qiladi. - Maxsus O'chiruvchilar:
unique_ptr
ga o'xshash maxsus o'chiruvchilarni qo'llab-quvvatlaydi.
std::shared_ptr
uchun Muhim Fikrlar:
- Tsiklik Bog'liqliklar: Ikki yoki undan ortiq obyektlar bir-biriga
shared_ptr
yordamida ishora qiladigan tsiklik bog'liqliklardan ehtiyot bo'ling. Bu xotira sizib chiqishiga olib kelishi mumkin, chunki havolalar soni hech qachon nolga yetmaydi. Bu tsikllarni buzish uchunstd::weak_ptr
dan foydalanish mumkin. - Ishlash samaradorligi yuklamasi: Havolalarni sanash oddiy ko'rsatkichlar yoki
unique_ptr
ga nisbatan biroz ishlash samaradorligi yuklamasini keltirib chiqaradi.
std::weak_ptr
: Egalik qilmaydigan Kuzatuvchi
std::weak_ptr
shared_ptr
tomonidan boshqariladigan obyektga egalik qilmaydigan havola beradi. U havola sanash mexanizmida ishtirok etmaydi, ya'ni barcha shared_ptr
nusxalari amal qilish doirasidan chiqqanda obyektning o'chirilishiga to'sqinlik qilmaydi. weak_ptr
obyektga egalik qilmasdan uni kuzatish uchun, ayniqsa tsiklik bog'liqliklarni buzish uchun foydalidir.
Misol: Tsiklik Bog'liqliklarni Buzish uchun std::weak_ptr
dan foydalanish
#include <iostream>
#include <memory>
class B;
class A {
public:
std::shared_ptr<B> b;
~A() { std::cout << "A yo'q qilindi" << std::endl; }
};
class B {
public:
std::weak_ptr<A> a; // Tsiklik bog'liqlikning oldini olish uchun weak_ptr dan foydalanish
~B() { std::cout << "B yo'q qilindi" << std::endl; }
};
int main() {
std::shared_ptr<A> a = std::make_shared<A>();
std::shared_ptr<B> b = std::make_shared<B>();
a->b = b;
b->a = a;
// weak_ptr bo'lmaganda, A va B tsiklik bog'liqlik tufayli hech qachon yo'q qilinmas edi
return 0;
} // A va B to'g'ri yo'q qilinadi
Misol: Obyekt Yaroqliligini Tekshirish uchun std::weak_ptr
dan foydalanish
#include <iostream>
#include <memory>
int main() {
std::shared_ptr<int> sharedPtr = std::make_shared<int>(123);
std::weak_ptr<int> weakPtr = sharedPtr;
// Obyekt hali ham mavjudligini tekshirish
if (auto observedPtr = weakPtr.lock()) { // lock() obyekt mavjud bo'lsa, shared_ptr qaytaradi
std::cout << "Obyekt mavjud: " << *observedPtr << std::endl; // Natija: Obyekt mavjud: 123
}
sharedPtr.reset(); // Egalikdan voz kechish
// sharedPtr tiklangandan keyin yana tekshirish
if (auto observedPtr = weakPtr.lock()) {
std::cout << "Obyekt mavjud: " << *observedPtr << std::endl; // Bu bajarilmaydi
} else {
std::cout << "Obyekt yo'q qilingan." << std::endl; // Natija: Obyekt yo'q qilingan.
}
return 0;
}
std::weak_ptr
ning Asosiy Xususiyatlari:
- Egalik qilmaslik: Havolalarni sanashda ishtirok etmaydi.
- Kuzatuvchi: Obyektga egalik qilmasdan uni kuzatish imkonini beradi.
- Tsiklik Bog'liqliklarni Buzish:
shared_ptr
tomonidan boshqariladigan obyektlar orasidagi tsiklik bog'liqliklarni buzish uchun foydalidir. - Obyekt Yaroqliligini Tekshirish: Obyektning hali ham mavjudligini tekshirish uchun
lock()
usulidan foydalanish mumkin, bu usul obyekt tirik bo'lsashared_ptr
ni, agar u yo'q qilingan bo'lsa, nullshared_ptr
ni qaytaradi.
To'g'ri Aqlli Ko'rsatkichni Tanlash
Tegishli aqlli ko'rsatkichni tanlash siz tatbiq etishingiz kerak bo'lgan egalik semantikasiga bog'liq:
unique_ptr
: Obyektga yagona egalik kerak bo'lganda foydalaning. Bu eng samarali aqlli ko'rsatkichdir va iloji boricha afzal ko'rilishi kerak.shared_ptr
: Bir nechta subyektlar obyektga egalikni bo'lishishi kerak bo'lganda foydalaning. Mumkin bo'lgan tsiklik bog'liqliklar va ishlash samaradorligi yuklamasini yodda tuting.weak_ptr
:shared_ptr
tomonidan boshqariladigan obyektni egalik qilmasdan kuzatish kerak bo'lganda, ayniqsa tsiklik bog'liqliklarni buzish yoki obyekt yaroqliligini tekshirish uchun foydalaning.
Aqlli Ko'rsatkichlardan Foydalanish bo'yicha Eng Yaxshi Amaliyotlar
Aqlli ko'rsatkichlarning afzalliklarini maksimal darajada oshirish va keng tarqalgan xatolardan qochish uchun quyidagi eng yaxshi amaliyotlarga rioya qiling:
std::make_unique
vastd::make_shared
ni afzal ko'ring: Bu funksiyalar istisnolar xavfsizligini ta'minlaydi va nazorat bloki hamda obyektni bitta xotira ajratishda ajratib, ishlash samaradorligini oshirishi mumkin.- Oddiy Ko'rsatkichlardan Qoching: Kodingizda oddiy ko'rsatkichlardan foydalanishni minimallashtiring. Iloji boricha dinamik ajratilgan obyektlarning yashash davrini boshqarish uchun aqlli ko'rsatkichlardan foydalaning.
- Aqlli Ko'rsatkichlarni Darhol Initsializatsiya Qiling: Initsializatsiya qilinmagan ko'rsatkich muammolarining oldini olish uchun aqlli ko'rsatkichlarni e'lon qilinishi bilanoq initsializatsiya qiling.
- Tsiklik Bog'liqliklarni Yodda tuting:
shared_ptr
tomonidan boshqariladigan obyektlar orasidagi tsiklik bog'liqliklarni buzish uchunweak_ptr
dan foydalaning. - Oddiy Ko'rsatkichlarni Egalikni Oluchi Funksiyalarga Uzatishdan Qoching: Tasodifiy egalikni o'tkazish yoki ikki marta o'chirish muammolaridan qochish uchun aqlli ko'rsatkichlarni qiymat yoki havola bo'yicha uzating.
Misol: std::make_unique
va std::make_shared
dan foydalanish
#include <iostream>
#include <memory>
class MyClass {
public:
MyClass(int value) : value_(value) {
std::cout << "MyClass qiymat bilan yaratildi: " << value_ << std::endl;
}
~MyClass() {
std::cout << "MyClass qiymat bilan yo'q qilindi: " << value_ << std::endl;
}
int getValue() const { return value_; }
private:
int value_;
};
int main() {
// std::make_unique dan foydalanish
std::unique_ptr<MyClass> uniquePtr = std::make_unique<MyClass>(50);
std::cout << "Unique pointer qiymati: " << uniquePtr->getValue() << std::endl;
// std::make_shared dan foydalanish
std::shared_ptr<MyClass> sharedPtr = std::make_shared<MyClass>(100);
std::cout << "Shared pointer qiymati: " << sharedPtr->getValue() << std::endl;
return 0;
}
Aqlli Ko'rsatkichlar va Istisnolar Xavfsizligi
Aqlli ko'rsatkichlar istisnolar xavfsizligiga sezilarli hissa qo'shadi. Dinamik ajratilgan obyektlarning yashash davrini avtomatik ravishda boshqarish orqali, ular istisno yuzaga kelgan taqdirda ham xotiraning bo'shatilishini ta'minlaydi. Bu xotira sizib chiqishining oldini oladi va ilovangizning yaxlitligini saqlashga yordam beradi.
Oddiy ko'rsatkichlardan foydalanganda xotiraning sizib chiqishi mumkin bo'lgan quyidagi misolni ko'rib chiqing:
#include <iostream>
void processData() {
int* data = new int[100]; // Xotira ajratish
// Istisno keltirib chiqarishi mumkin bo'lgan ba'zi operatsiyalarni bajarish
try {
// ... potentsial istisno keltirib chiqaruvchi kod ...
throw std::runtime_error("Nimadir noto'g'ri ketdi!"); // Misol istisno
} catch (...) {
delete[] data; // catch blokida xotirani bo'shatish
throw; // Istisnoni qayta yuborish
}
delete[] data; // Xotirani bo'shatish (faqat istisno yuzaga kelmasa erishiladi)
}
Agar try
bloki ichida birinchi delete[] data;
iborasidan *oldin* istisno yuzaga kelsa, data
uchun ajratilgan xotira sizib chiqadi. Aqlli ko'rsatkichlardan foydalanib, buning oldini olish mumkin:
#include <iostream>
#include <memory>
void processData() {
std::unique_ptr<int[]> data(new int[100]); // Aqlli ko'rsatkich yordamida xotira ajratish
// Istisno keltirib chiqarishi mumkin bo'lgan ba'zi operatsiyalarni bajarish
try {
// ... potentsial istisno keltirib chiqaruvchi kod ...
throw std::runtime_error("Nimadir noto'g'ri ketdi!"); // Misol istisno
} catch (...) {
throw; // Istisnoni qayta yuborish
}
// data'ni aniq o'chirishga hojat yo'q; unique_ptr buni avtomatik ravishda bajaradi
}
Ushbu yaxshilangan misolda, unique_ptr
data
uchun ajratilgan xotirani avtomatik ravishda boshqaradi. Agar istisno yuzaga kelsa, stek ochilishi paytida unique_ptr
ning destruktori chaqiriladi, bu esa istisno ushlanishi yoki qayta yuborilishidan qat'i nazar, xotiraning bo'shatilishini ta'minlaydi.
Xulosa
Aqlli ko'rsatkichlar xavfsiz, samarali va qo'llab-quvvatlanadigan C++ kodini yozish uchun asosiy vositalardir. Xotira boshqaruvini avtomatlashtirish va RAII tamoyiliga rioya qilish orqali, ular oddiy ko'rsatkichlar bilan bog'liq keng tarqalgan muammolarni bartaraf etadi va yanada mustahkam ilovalarga hissa qo'shadi. Aqlli ko'rsatkichlarning har xil turlarini va ularning tegishli qo'llanilish holatlarini tushunish har bir C++ dasturchisi uchun zarurdir. Aqlli ko'rsatkichlarni o'zlashtirish va eng yaxshi amaliyotlarga rioya qilish orqali siz xotira sizib chiqishini, osilib qolgan ko'rsatkichlarni va boshqa xotira bilan bog'liq xatolarni sezilarli darajada kamaytirishingiz mumkin, bu esa yanada ishonchli va xavfsiz dasturiy ta'minotga olib keladi.
Silikon Vodiysida yuqori unumdorlikdagi hisoblashlar uchun zamonaviy C++ dan foydalanayotgan startaplardan tortib, muhim tizimlarni ishlab chiquvchi global korxonalargacha, aqlli ko'rsatkichlar universal qo'llaniladi. Siz Buyumlar Interneti uchun o'rnatilgan tizimlarni qurayotgan bo'lasizmi yoki ilg'or moliyaviy ilovalarni ishlab chiqayotgan bo'lasizmi, aqlli ko'rsatkichlarni o'zlashtirish mukammallikka intilayotgan har qanday C++ dasturchisi uchun asosiy mahoratdir.
Qo'shimcha O'rganish uchun
- cppreference.com: https://en.cppreference.com/w/cpp/memory
- Effective Modern C++ by Scott Meyers
- C++ Primer by Stanley B. Lippman, Josée Lajoie, and Barbara E. Moo