72 / 90

C++ Exception Handling

C++ istisnolarni boshqarish

Istisno - bu dasturni bajarish jarayonida yuzaga keladigan kutilmagan hodisa. Masalan,

example.cpp
double divide_by_zero = 7 / 0;

Yuqoridagi kod istisnoga sabab bo'ladi, chunki raqamni 0 ga bo'lish mumkin emas.

C++ da bu turdagi xatolarni qayta ishlash jarayoni istisnolardan foydalanish deb nomlanadi.

C++ tilida biz try va catch bloklari hamda throw kalit so‘zi yordamida istisnolarni hal qilamiz.

  • try - code that may raise an exception -throw - xato aniqlanganda istisno qiladi -catch - throw kalit so'zi tomonidan chiqarilgan istisnoni hal qiluvchi kod

[!NOTE] throw operatori majburiy emas, ayniqsa standart C++ istisnolaridan foydalansak.

C++ da istisnolardan foydalanish sintaksisi

C++ da istisnolarni qayta ishlash uchun asosiy sintaksis quyida keltirilgan:

example.cpp
try {

    // istisno keltirishi mumkin bo'lgan kod
    throw argument;
}

catch (exception) {
    // istisnolarni hal qilish uchun kod
}

Bu erda biz try blokiga istisno yaratishi mumkin bo'lgan kodni joylashtirdik. Har bir try blokidan keyin catch bloki keladi.

Istisno yuzaga kelganda, throw operatori catch bloki tomonidan ushlab turiladigan istisnoni chiqaradi.

catch blokini try blokisiz ishlatib bo‘lmaydi.

1-misol: C++ istisnolardan foydalanish

example.cpp
// Ikki sonni bo'lish uchun dastur
// bo'linuvchi 0 bo'lsa, istisno qiladi

#include <iostream>
using namespace std;

int main() {

    double numerator, denominator, divide;

    cout << "Enter numerator: ";
    cin >> numerator;

    cout << "Enter denominator: ";
    cin >> denominator;

    try {

        // agar maxraj 0 bo'lsa, istisno qiling
        if (denominator == 0)
            throw 0;

        // agar maxraj 0 bo'lsa, bajarilmaydi
        divide = numerator / denominator;
        cout << numerator << " / " << denominator << " = " << divide << endl;
    } 

    catch (int num_exception) {
        cout << "Error: Cannot divide by " << num_exception << endl;
    }

    return 0;
}

Chiqish 1

kod
Enter numerator: 72
Enter denominator: 0
Error: Cannot divide by 0

Chiqish 2

kod
Enter numerator: 72
Enter denominator: 3
72 / 3 = 24

Yuqoridagi dastur ikkita raqamni ajratadi va natijani ko'rsatadi. Ammo maxraj 0 bo'lsa, istisno sodir bo'ladi.

Istisnoni hal qilish uchun biz divide = numerator / denominator; kodini sinash blokiga joylashtirdik. Endi, istisno sodir bo'lganda, try blokidagi kodning qolgan qismi o'tkazib yuboriladi.

catch bloki tashlangan istisnoni ushlaydi va uning ichidagi operatorlarni bajaradi.

Agar try blokidagi operatorlarning hech biri istisno yaratmasa, catch bloki o'tkazib yuboriladi.

Working of try, throw, and catch statements in C++

C++ try, throw and catch Statements

E'tibor bering, biz int literal 0 ni throw 0; kodi bilan tashladik.

catch parametri int num_exception throw operatori tomonidan berilgan qiymatni, ya'ni 0 harfini oladi.

Istisnolarning barcha turlarini ushlash

Istisnolarni ko'rib chiqishda try operatorimizdagi kod tufayli yuzaga kelishi mumkin bo'lgan istisnolar turlarini bilishimiz muhimdir.

Bu biz tegishli catch parametrlaridan foydalanishimiz uchun. Aks holda, try...catch operatorlari to'g'ri ishlamasligi mumkin.

Agar biz try blokimizda yuzaga kelishi mumkin bo'lgan istisnolar turlarini bilmasak, catch parametrimiz sifatida ... ellips belgisidan foydalanishimiz mumkin.

example.cpp
try {
    // kod
}
catch (...) {
    // kod
}

C++ Bir nechta catch bloklari

C++ da biz bitta kod blokidan kelib chiqadigan turli xil istisnolar uchun bir nechta catch iboralaridan foydalanishimiz mumkin.

example.cpp
try {
    // kod
} 
catch (exception1) {
    // kod
} 
catch (exception2) {
    // kod
} 
catch (...) {
    // kod
}

Bu yerda, agar istisno yuzaga kelsa, dasturimiz exception1 ni ushlaydi. Aks holda, u sodir bo'lsa, exception2 ni ushlaydi.

Agar exception1 ham, exception2 ham bo'lmagan xatolik bo'lsa, catch (...) {} ichidagi kod bajariladi.

[!NOTE] Eslatmalar: catch (...) {} har doim bizning try...catch operatorimizdagi yakuniy blok bo'lishi kerak. Buning sababi shundaki, bu blok barcha mumkin bo'lgan istisnolarni ushlaydi va standart catch bloki sifatida ishlaydi. Kodimizga standart catch blokini kiritish shart emas.

2-misol: C++ bir nechta catch bloklari

Bu dastur ikkita raqamni ajratadi va natijani massiv elementida saqlaydi. Ushbu dasturda yuzaga kelishi mumkin bo'lgan ikkita istisno mavjud:

  • Agar massiv chegaradan tashqarida bo'lsa, ya'ni massiv indeksi massiv hajmidan katta bo'lsa
  • Agar raqam 0 ga bo'lingan bo'lsa

Ushbu istisnolar bir nechta catch operatorlarida ushlangan.

example.cpp
#include <iostream>
using namespace std;

int main() {
    
    double numerator, denominator, arr[4] = {0.0, 0.0, 0.0, 0.0};
    int index;
    
    cout << "Enter array index: ";
    cin >> index;
    
    try {
        
        // agar massiv chegaradan tashqarida bo'lsa, istisno qiling
        if (index >= 4)
            throw "Error: Array out of bounds!";
            
        // massiv chegaradan tashqarida bo'lsa, bajarilmaydi
        cout << "Enter numerator: ";
        cin >> numerator;
    
        cout << "Enter denominator: ";
        cin >> denominator;

        // agar maxraj 0 bo'lsa, istisno qiling
        if (denominator == 0)
            throw 0;

        // agar maxraj 0 bo'lsa, bajarilmaydi
        arr[index] = numerator / denominator;
        cout << arr[index] << endl;
    }

    // catch "Masiv chegaradan tashqarida" istisnosi
    catch (const char* msg) {
        cout << msg << endl;
    }

    // catch "0 ga bo'lish" istisnosi
    catch (int num) {
        cout << "Error: Cannot divide by " << num << endl;
    }

    // boshqa har qanday istisnoni ushlang
    catch (...) {
        cout << "Unexpected exception!" << endl;
    }
    
    return 0;    
}

Chiqish 1

kod
Enter array index: 5
Error: Array out of bounds!

Bu yerda arr massivi faqat 4 ta elementga ega. Shunday qilib, indeks 3 dan katta bo'lishi mumkin emas.

Bu holda, indeks 5. Shunday qilib, biz bir satr literal otish "Error: Array out of bounds!" . Ushbu istisno birinchi catch bloki tomonidan ushlangan.

catch parametriga e'tibor bering const char* msg . Bu catch operatori argument sifatida string literalini olishini ko'rsatadi.

Chiqish 2

kod
Enter array index: 2
Enter numerator: 5
Enter denominator: 0
Error: Cannot divide by 0

Bu erda maxraj 0 ga teng. Shunday qilib, biz int literal 0 ni tashlaymiz. Bu istisno ikkinchi catch bloki tomonidan ushlangan.

Agar boshqa istisnolar yuzaga kelsa, u standart catch bloki tomonidan ushlanadi.

Chiqish 3

kod
Enter array index: 2
Enter numerator: 5
Enter denominator: 2
2.5

Bu erda dastur hech qanday muammosiz ishlaydi, chunki istisno bo'lmaydi.

C++ standart istisno

C++ bizni istisnolardan foydalanishda foydalanishimiz mumkin bo'lgan bir qator standart istisnolarni taqdim etdi. Ulardan ba'zilari quyidagi jadvalda keltirilgan.

Istisno Tavsif std::exception Barcha C++ istisnolarining asosiy klassi. std::bad_alloc Dinamik xotira taqsimoti bajarilmaganda tashlanadi. std::bad_cast dynamic_cast ni yaroqsiz turga bajarishga urinilganda C++ tomonidan tashlanadi. std::bad_exception Odatda istisno o'rnatilganda va uni qayta tiklash mumkin bo'lmaganda tashlanadi.

C++ da boshqa ko'plab standart istisnolar mavjud.

Ushbu istisnolar exception sarlavha faylida belgilangan.

Batafsil ma’lumot olish uchun C++ standart istisnolar qo‘llanmasiga tashrif buyuring.