Dinamik xotira taqsimoti O'zgaruvchilarning dinamik taqsimlanishi, statik va avtomatik xotira ajratish ikkita umumiy xususiyatga ega:
Dinamik xotirani taqsimlash qanday ishlaydi?
Sizning kompyuteringizda dasturlar tomonidan foydalanish mumkin bo'lgan xotira mavjud (ehtimol uning ko'pi). Dasturni ishga tushirganingizda, operatsion tizimingiz ushbu dasturni xotiraning bir qismiga yuklaydi. Va sizning dasturingiz tomonidan ishlatiladigan bu xotira bir necha qismlarga bo'linadi, ularning har biri ma'lum bir vazifani bajaradi. Bir qism sizning kodingizni o'z ichiga oladi, ikkinchisi oddiy operatsiyalarni bajarish uchun ishlatiladi (deb nomlangan funktsiyalarni kuzatish, global va mahalliy o'zgaruvchilarni yaratish va yo'q qilish va h.k.). Bu haqda keyinroq gaplashamiz. Biroq, mavjud xotiraning aksariyati dasturlardan ajratish so'rovlarini kutmoqda.
Xotirani dinamik ravishda taqsimlaganingizda, siz operatsion tizimdan ushbu xotiraning bir qismini dasturingiz foydalanishi uchun zaxiralashni so'raysiz. Agar OS ushbu so'rovni bajara olsa, u holda ushbu xotira manzili dasturingizga qaytariladi. Bundan buyon sizning dasturingiz ushbu xotiradan xohlagancha foydalana oladi. Agar siz ushbu xotira bilan zarur bo'lgan hamma narsani bajargan bo'lsangiz, u boshqa so'rovlar orasida tarqatish uchun operatsion tizimga qaytarilishi kerak.
Statik yoki avtomatik xotira ajratishdan farqli o'laroq, dastur dinamik ravishda ajratilgan xotirani so'rash va qaytarish uchun javobgardir.
Xotirani bo'shatish
O'zgaruvchini dinamik ravishda taqsimlaganingizda, uni bir xil ishga tushirish bilan ham ishga tushirishingiz mumkin (C++11 da):
int *ptr1 = new int(7); // to'g'ridan-to'g'ri ishga tushirishdan foydalaning int *ptr2 = new int ( 8 ); // yagona ishga tushirishdan foydalaning
Agar dinamik ravishda ajratilgan o'zgaruvchi bilan kerak bo'lgan hamma narsa allaqachon bajarilgan bo'lsa, siz C++ ga ushbu xotirani bo'shatishni aniq aytishingiz kerak. O'zgaruvchilar uchun bu bilan amalga oshiriladi operator o'chirish:
// ptr yangi o'chirish ptr bilan allaqachon ajratilgan deb faraz qiling; // ptr tomonidan ko'rsatilgan xotirani operatsion tizimga qaytarish ptr = 0; // ptr null (C++ 11 da 0 o'rniga nullptr dan foydalaning)
O'chirish operatori aslida hech narsani o'chirmaydi. Bu shunchaki operatsion tizimga oldindan ajratilgan xotirani qaytaradi. Keyin operatsion tizim ushbu xotirani boshqa dasturga (yoki yana o'sha ilovaga) qayta belgilashi mumkin.
Garchi biz olib tashlayotgandek tuyulishi mumkin o'zgaruvchan, Ammo bu unday emas! Ko'rsatkich o'zgaruvchisi avvalgidek bir xil qamrovga ega va har qanday boshqa o'zgaruvchi kabi yangi qiymat tayinlanishi mumkin.
Dinamik ravishda ajratilgan xotiraga ishora qilmaydigan ko'rsatgichni o'chirish muammolarga olib kelishi mumkinligini unutmang.
osilgan ko'rsatkichlar
C++ bo'shatilgan xotira tarkibiga yoki o'chirilayotgan ko'rsatkich qiymatiga nima bo'lishi haqida hech qanday kafolat bermaydi. Ko'pgina hollarda, operatsion tizimga qaytarilgan xotira oldingi qiymatlarni o'z ichiga oladi ozod qilish, va ko'rsatkich faqat allaqachon bo'shatilgan (o'chirilgan) xotiraga ishora qilishda davom etadi.
Bo'shatilgan xotiraga ishora qiluvchi ko'rsatgich chaqiriladi osilgan ko'rsatgich. Osilib turgan ko'rsatkichni yo'qotish yoki o'chirish kutilmagan natijalarga olib keladi. Quyidagi dasturni ko'rib chiqing:
#o'z ichiga oladi int main() ( int *ptr = new int; *ptr = 8; // qiymatni ajratilgan xotira joyiga qo'ying delete ptr; // xotirani operatsion tizimga qaytaring. ptr endi osilgan ko'rsatkich std:: cout<< *ptr; // разыменование висячего указателя приведёт к неожиданным результатам delete ptr; // попытка освободить память снова приведёт к неожиданным результатам также return 0; }
#o'z ichiga oladi
int main()
int * ptr = new int; // butun son o'zgaruvchini dinamik ravishda taqsimlash
* ptr = 8; // qiymatni ajratilgan xotira joyiga qo'ying
ptrni o'chirish; // xotirani operatsion tizimga qaytarish. ptr endi osilgan ko'rsatkichdir
std :: cout<< * ptr ; // osilgan ko'rsatgichga havolani bekor qilish kutilmagan natijalarga olib keladi
ptrni o'chirish; // xotirani yana bo'shatishga urinish ham kutilmagan natijalarga olib keladi
qaytish 0 ;
Yuqoridagi dasturda dinamik o'zgaruvchiga oldindan tayinlangan 8 qiymati bo'shatilgandan keyin ham bo'lishi yoki bo'lmasligi mumkin. Bundan tashqari, bo'shatilgan xotira allaqachon boshqa dasturga (yoki operatsion tizimning shaxsiy foydalanishi uchun) ajratilgan bo'lishi mumkin va unga kirishga urinish operatsion tizimning dasturingizni avtomatik ravishda to'xtatishiga olib keladi.
Xotirani bo'shatish jarayoni ham yaratilishiga olib kelishi mumkin bir nechta osilgan ko'rsatkichlar. Quyidagi misolni ko'rib chiqing:
#o'z ichiga oladi int main() ( int *ptr = new int; // butun son o'zgaruvchisini dinamik ravishda ajratish int *otherPtr = ptr; // otherPtr endi ptr delete ptr kabi ajratilgan xotiraga ishora qiladi; // xotirani operatsion tizimga qaytarish. ptr va otherPtr endi osilgan ko'rsatkichlar ptr = 0; // ptr endi nullptr // Biroq, otherPtr hali ham osilgan ko'rsatkichdir! return 0; )
#o'z ichiga oladi
int main()
int * ptr = new int; // butun son o'zgaruvchini dinamik ravishda taqsimlash
int * otherPtr = ptr; // otherPtr endi ptr bilan bir xil ajratilgan xotiraga ishora qiladi
ptrni o'chirish; // xotirani operatsion tizimga qaytarish. ptr va otherPtr endi osilgan ko'rsatkichlardir
ptr = 0; // ptr endi nullptr
// Biroq, otherPtr hali ham osilgan ko'rsatkichdir!
qaytish 0 ;
Birinchidan, bir nechta ko'rsatkichlar ajratilgan xotiraning bir qismiga ishora qiladigan vaziyatlardan qochishga harakat qiling. Agar buning iloji bo'lmasa, unda qaysi ko'rsatgich xotiraga "egalik qiladi" (va uni o'chirish uchun javobgardir) va qaysi ko'rsatkichlar unga kirishini aniqlang.
Ikkinchidan, ko'rsatgichni o'chirib tashlaganingizda va agar u o'chirilgandan keyin darhol chiqmasa, u null bo'lishi kerak, ya'ni. 0 qiymatini belgilang (yoki C++ 11 da). "O'chirilgandan so'ng darhol ko'lamdan tashqarida" deganda men ko'rsatgichni u e'lon qilingan blokning eng oxirida o'chirishingizni nazarda tutyapman.
Qoida: O'chirilgan ko'rsatgichlarni 0 ga (yoki C++11 da nullptr) o'rnating, agar ular o'chirilgandan so'ng darhol ko'lamdan chiqib ketsa.
yangi operator
Operatsion tizimdan xotira so'ralganda, kamdan-kam hollarda u mavjud bo'lmasligi mumkin (ya'ni, mavjud bo'lmasligi mumkin).
Odatiy bo'lib, agar yangi operator ishlamasa, xotira ajratilmagan, keyin a istisno bad_alloc. Agar bu istisno to'g'ri ishlanmasa (bu shunday bo'ladi, chunki biz hali istisnolarni ko'rib chiqmaganmiz va ularni ko'rib chiqmaganmiz), u holda dastur ishlov berilmagan istisno xatosi bilan bekor qilinadi (halokatga uchraydi).
Ko'pgina hollarda, yangi operator bilan istisno qilish jarayoni (shuningdek, dasturni buzish) istalmagan, shuning uchun yangi operatorning muqobil shakli mavjud bo'lib, agar xotira ajratilmasa, null ko'rsatkichni qaytaradi. Siz shunchaki qo'shishingiz kerak std::nothrow doimiy yangi kalit so'z va ma'lumotlar turi o'rtasida:
int *qiymat = new (std::nothrow) int; // Agar butun o'zgaruvchining dinamik taqsimlanishi bajarilmasa, ko'rsatkich qiymati nolga aylanadi
Yuqoridagi misolda, agar new dinamik ravishda ajratilgan xotiraga ega ko'rsatgichni qaytarmasa, u holda null ko'rsatgich qaytariladi.
Undan voz kechish ham tavsiya etilmaydi, chunki bu kutilmagan natijalarga olib keladi (ehtimol, dasturda ishdan chiqish). Shuning uchun, eng yaxshi amaliyot xotirani ajratish bo'yicha barcha so'rovlarni tekshirish, bu so'rovlar muvaffaqiyatli bajarilishini va xotira ajratilganligini ta'minlashdir:
int *qiymat = new (std::nothrow) int; // butun son qiymati uchun dinamik xotirani ajratishni so'rash, agar (!qiymat) // agar yangi null qaytarsa (ya'ni, xotira ajratilmagan) ishni ko'rib chiqamiz ( // Ushbu holatni boshqarish std::cout<< "Could not allocate memory"; }
Yangi operator tomonidan xotira ajratilmasligi juda kamdan-kam sodir bo'lganligi sababli, dasturchilar odatda bu tekshirishni unutishadi!
Null ko'rsatkichlar va dinamik xotira ajratish
Null ko'rsatkichlar (qiymati 0 yoki nullptr bo'lgan ko'rsatkichlar) dinamik xotirani ajratishda ayniqsa foydalidir. Ularning mavjudligi, go'yo bizga aytadi: "Ushbu ko'rsatkichga xotira ajratilmagan". Va bu, o'z navbatida, shartli xotira ajratishni amalga oshirish uchun ishlatilishi mumkin:
// Agar ptr xotira hali ajratilmagan bo'lsa, uni ajrating, agar (!ptr) ptr = new int;
Null ko'rsatgichni olib tashlash hech narsaga ta'sir qilmaydi. Shunday qilib, quyidagilar kerak emas:
agar (ptr) ptrni o'chirish;
agar (ptr)
ptrni o'chirish;
Buning o'rniga siz shunchaki yozishingiz mumkin:
ptrni o'chirish;
Agar ptr null bo'lmasa, dinamik ravishda ajratilgan o'zgaruvchi o'chiriladi. Ko'rsatkich qiymati null bo'lsa, hech narsa bo'lmaydi.
Xotiraning oqishi
Dinamik ravishda ajratilgan xotira hech qanday qamrovga ega emas, ya'ni. u aniq bo'shatilguncha yoki dasturingiz uning bajarilishini to'xtatmaguncha (va operatsion tizim barcha xotira buferlarini o'zi o'chiradi) ajratilgan bo'lib qoladi. Biroq, dinamik ravishda ajratilgan xotira manzillarini saqlash uchun ishlatiladigan ko'rsatkichlar oddiy o'zgaruvchilar doirasini aniqlash qoidalariga amal qiladi. Bu nomuvofiqlik qiziqarli xatti-harakatlarga olib kelishi mumkin. Masalan:
void doSomething() ( int *ptr = new int; )