MA’RUZA MATERIALLARI
16-MA’RUZA. PARAMETRIZATSIYA VA VORISLIK TAMOYILLARINI HAMKORLIKDA QO’LLASH. PARAMETRLANGAN SINFLARNING TARKIBIY QISMLARIGA TASHQI MUROJAATLARNI TASHKIL QILISH. PARAMETRLANGAN SINFLAR VA STATIK ELEMENTLAR
17-MA’RUZA. ISTISNOLI HOLATLAR VA ULARNI QAYTA ISHLASH. ISTISNOLI HOLATLAR ASOSLARI. ISTISNOLI HOLATLARNI GENERASIYA QILISH. ISTISNOLI HOLATLARNI USHLASH.
Istisnolar. Ular nima uchun kerak?
Biz avvalroq C++ xatolarni boshqarish mexanizmlari haqida gapirgan edik, masalan, cerr(), exit() va assert(). Biroq, biz yana bir juda muhim mavzu - "C++ tilidagi istisnolar" haqida gapirishga ulgurmadik. Biz buni hozir tuzatamiz.
Qaytish kodlari ishlamasa
Qayta foydalanish mumkin bo'lgan kodni yozishda xatolarni qayta ishlashga ehtiyoj bor. Mumkin bo'lgan xatolarni hal qilishning eng keng tarqalgan usullaridan biri bu return operatori qaytaradigan qaytarish kodlaridan (yoki "chiqish kodlari") foydalanishdir. Masalan:
int findFirstChar(const char* string, char ch)
{
// Satrdagi har bir belgini ko'rib chiqish
for (int index=0; index < strlen(string); ++index)
// Agar joriy belgi ch o'zgaruvchining qiymatiga
//mos kelsa, u holda ushbu belgi indeksini qaytaring
if (string[index] == ch)
return index;
// Agar mos keladigan topilmasa, -1 ni qaytaring
return -1;
}
Bu funksiya uzatilayotgan satrning ch o‘zgaruvchisi qiymatiga mos keladigan birinchi belgisi indeksini qaytaradi. Agar belgi topilmasa, funktsiya xato ko'rsatkichi sifatida -1 ni qaytaradi.
Ushbu yondashuvning asosiy afzalligi uning soddaligi. Biroq, ahamiyatsiz bo'lmagan holatlarda tezda o'zini namoyon qiladigan bir qator kamchiliklar mavjud.
Birinchidan, qaytarish qiymatlari har doim ham aniq emas. Agar funktsiya -1 ni qaytarsa, bu muayyan xatoni bildiradimi yoki bu haqiqiy qaytarish qiymatimi? Ko'pincha ko'z oldingizda funktsiyaning kodini ko'rmasdan tushunish qiyin.
Ikkinchidan, funksiyalar faqat bitta qiymatni qaytarishi mumkin. Ammo biz funksiyani bajarish natijasini ham, chiqish kodini ham qaytarishimiz kerak bo'lsa-chi? Masalan:
double divide(int a, int b)
{
return static_cast(a)/b;
}
Bu yerda xatoliklarni qayta ishlash mexanizmi kerak, chunki agar foydalanuvchi b parametri sifatida 0 ni o'tkazsa, u muvaffaqiyatsiz bo'ladi. Bundan tashqari, funktsiya static_cast (a) / b operatsiyasining natijasini ham qaytarishi kerak. Buni qanday qilish mumkin? Variantlardan biri amal natijasini yoki yakuniy kodni mos yozuvlar bo'yicha qaytarishdir, masalan:
#include
using namespace std;
double divide(int a, int b, bool &success)
{
if (b == 0)
{
success = false;
return 0.0;
}
success = true;
return static_cast(a)/b;
}
int main()
{
bool success;
double result = divide(7, 0, success);
// operatsiya muvaffaqiyatli bo'ladimi yoki yo'qligini
//oldindan bilish uchun biz hozir bool qiymatini o'tkazmoqdamiz
if (!success)
// resultdan foydalanishdan oldin operatsiya natijasini tekshiring
cerr << "Xatolik yuz berdi" << endl;
else
cout << "Natija: " << result << '\n';
}
Uchinchidan, juda ko'p kod mavjud bo'lganda, ko'p narsalar noto'g'ri ketishi mumkin, shuning uchun qaytarish kodlari doimo tekshirilishi kerak. Matn faylini ma'lum qiymatlar mavjudligi uchun tahlil qiladigan quyidagi dastur fragmentini ko'rib chiqaylik:
ifstream setupIni("setup.ini"); // o'qish uchun setup.ini oching
// Agar faylni ochib bo'lmasa (masalan, u yetishmayotganligi sababli),
//biz xatoni qaytaramiz
if (!setupIni)
return ERROR_OPENING_FILE;
// Agar faylni ochish mumkin bo'lsa, ushbu fayldagi qiymatlarni o'qing
if (!readIntegerFromFile(setupIni, m_firstParameter)) // faylda int qiymatini topishga harakat qilish
return ERROR_READING_VALUE; // agar qiymat topilmasa, xatoni qaytaring
if (!readDoubleFromFile(setupIni, m_secondParameter)) // faylda double turi qiymatini topishga harakat qilish
return ERROR_READING_VALUE;
if (!readFloatFromFile(setupIni, m_thirdParameter)) // faylda float qiymatini topishga harakat qilish
return ERROR_READING_VALUE;
Biz hali fayllar bilan ishlashni ko'rib chiqmadik, shuning uchun bu yerda qanday va nima ishlashini tushunmasangiz, tashvishlanmang - shunchaki esda tutingki, har bir funksiya chaqiruvi holatini tekshirish va murojaat qiluvchiga qaytarishni talab qiladi. Endi tasavvur qiling-a, agar bizda har xil turdagi yigirmata parametr bo'lsa - biz ERROR_READING_VALUEni yigirma marta tekshirishimiz va qaytarishimiz kerak edi! Xatolarni hal qilishning butun mexanizmi bu funktsiya aslida nima qilishi kerakligini tushunishni (o'qishni) qiyinlashtiradi.
To'rtinchidan, qaytarish kodlari konstruktorlar bilan yaxshi o'ynamaydi. Agar biz obyekt yaratsak va konstruktor ichida halokatli narsa sodir bo'lsa nima bo'ladi? Konstruktorlar holat indikatorini qaytarish uchun return operatoridan foydalana olmaydi va havola orqali o'tish juda ko'p noqulayliklar keltirib chiqarishi mumkin va aniq tekshirilishi kerak. Bunga qo'shimcha ravishda, agar biz buni qilsak ham, ob'ekt baribir yaratiladi va biz oqibatlarini allaqachon ko'rib chiqamiz (yoki qayta ishlash yoki o'chirish).
Nihoyat, xatoni caller ga qaytarishda, callerning o'zi har doim ham xatoni hal qilishga tayyor bo'lmasligi mumkin. Agar murojaat qiluvchi xatoni hal qilishni istamasa, u yoki uni e'tiborsiz qoldiradi (bu allaqachon yomon) yoki xatoni uni qabul qilgan funktsiyaga qaytaradi. Bu nafaqat noqulay, balki dasturning ishdan chiqishiga yoki aniqlanmagan natijalarga olib kelishi mumkin.
Qaytish kodlari bilan bog'liq asosiy muammo shundaki, ular kodni bajarishning umumiy oqimi bilan chambarchas bog'langan va bu, o'z navbatida, bizning imkoniyatlarimizni cheklaydi.
Do'stlaringiz bilan baham: |