Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

معالجة الاستثناءات في OOP

جزء من قسم البرمجة كائنية التوجه في رحلة C++ على Coddy. الدرس 81 من 104.

توفّر معالجة الاستثناءات في C++ طريقةً منظّمة لاكتشاف أخطاء وقت التشغيل والاستجابة لها. في البرمجة كائنية التوجّه، تتكامل الاستثناءات طبيعيًا مع تسلسلات class، مما يتيح لك إنشاء أنواع أخطاء ذات معنى تحمل سياقًا حول ما حدث من خطأ.

تستخدم الآلية الأساسية ثلاث كلمات مفتاحية: try وthrow وcatch. يوضع الكود الذي قد يفشل داخل كتلة try، ويتم الإبلاغ عن الأخطاء باستخدام throw، بينما تتعامل كتل catch مع أنواع محددة من الاستثناءات:

#include <iostream>
#include <stdexcept>

class BankAccount {
    double balance;
public:
    BankAccount(double b) : balance(b) {}
    
    void withdraw(double amount) {
        if (amount > balance) {
            throw std::runtime_error("Insufficient funds");
        }
        balance -= amount;
    }
    
    double getBalance() const { return balance; }
};

int main() {
    BankAccount account(100.0);
    
    try {
        account.withdraw(150.0);
    } catch (const std::runtime_error& e) {
        std::cout << "Error: " << e.what() << "\n";
    }
    
    std::cout << "Balance: " << account.getBalance() << "\n";
}

توفر مكتبة C++ القياسية تسلسلاً هرميًا من فئات exception، تتجذر في std::exception. تشمل الفئات المشتقة الشائعة std::runtime_error لمشكلات وقت التشغيل وstd::logic_error لأخطاء البرمجة. توفر جميع exception القياسية method باسم what() يعيد message خطأ.

عند طرح استثناء، تتراجع مكدسة الاستدعاءات - ويتم استدعاء المُدمِّرات لجميع الكائنات المحلية بينما يبحث البرنامج عن كتلة catch مطابقة. يضمن ذلك التنظيف السليم حتى عند حدوث أخطاء، ولهذا السبب يعمل كلٌّ من RAII ومعالجة الاستثناءات معًا بشكل جيد.

challenge icon

التحدي

سهل

لنَبْنِ حاسبة قسمة آمنة توضّح معالجة الاستثناءات في سياق كائني التوجه. ستنشئ class باسم Calculator ينفّذ عمليات القسمة ويرمي استثناءات ذات معنى عند حدوث خطأ ما، ثم تعالج تلك الاستثناءات بسلاسة في برنامجك الرئيسي.

ستنظّم الكود عبر ثلاثة ملفات:

  • Calculator.h: Define class Calculator الذي ينفّذ العمليات الرياضية مع معالجة مناسبة للأخطاء.

    يجب أن يحتوي Calculator على method باسم divide(double numerator, double denominator) يعيد نتيجة القسمة. ومع ذلك، إذا كان denominator يساوي صفراً، فيجب أن يرمي std::runtime_error مع message Division by zero. وإذا كان numerator أو denominator سالباً، فيجب أن يرمي std::invalid_argument مع message Negative values not allowed.

    أضف أيضاً method باسم safeSqrt(double value) يعيد الجذر التربيعي للقيمة. إذا كانت القيمة سالبة، فارْمِ std::runtime_error مع message Cannot compute square root of negative number. ستحتاج إلى تضمين <cmath> من أجل function sqrt.

  • Calculator.cpp: Implement الـ methods المعلنة في ملف الرأس. احرص على تضمين ملفات الرأس اللازمة (<stdexcept> و<cmath>) والتحقق من الشروط قبل تنفيذ العمليات.
  • main.cpp: اقرأ ثلاثة inputs (كل واحد في سطر منفصل):
    1. نوع العملية: إما divide أو sqrt
    2. العدد الأول (double)
    3. العدد الثاني (double): يُستخدم فقط للقسمة، ويتم تجاهله مع sqrt

    أنشئ كائناً من Calculator ونفّذ العملية المطلوبة داخل كتلة try. إذا نجحت العملية، اطبع Result: [value]. وإذا تم التقاط exception، فاطبع Error: [message]، حيث تأتي message من method what() الخاصة بالاستثناء.

    بعد كتلة try-catch (بغض النظر عما إذا حدث exception أم لا)، اطبع Calculation complete لتوضيح أن البرنامج يواصل التنفيذ بعد معالجة الاستثناءات.

على سبيل المثال، عند استخدام inputs divide و10 و2:

Result: 5
Calculation complete

عند استخدام inputs divide و10 و0:

Error: Division by zero
Calculation complete

عند استخدام inputs divide و-5 و2:

Error: Negative values not allowed
Calculation complete

عند استخدام inputs sqrt و16 و0:

Result: 4
Calculation complete

عند استخدام inputs sqrt و-9 و0:

Error: Cannot compute square root of negative number
Calculation complete

لاحظ كيف يتعامل البرنامج بسلاسة مع الأخطاء من خلال التقاط exceptions وعرض messages ذات معنى، ثم يواصل التنفيذ. يحافظ هذا النمط على نظافة معالجة الأخطاء لديك، ويُبقي منطقك الرئيسي مركزاً على المسار السليم.

جرّب بنفسك

#include <iostream>
#include <string>
#include "Calculator.h"

using namespace std;

int main() {
    // اقرأ المدخلات
    string operation;
    double num1, num2;
    
    cin >> operation;
    cin >> num1;
    cin >> num2;
    
    // أنشئ كائن Calculator
    Calculator calc;
    
    // TODO: استخدم كتلة try-catch لتنفيذ العملية
    // - إذا كانت العملية "divide"، استدعِ calc.divide(num1, num2)
    // - إذا كانت العملية "sqrt"، استدعِ calc.safeSqrt(num1)
    // - عند النجاح، اطبع "Result: " متبوعًا بالنتيجة
    // - التقط الاستثناءات واطبع "Error: " متبوعًا برسالة الاستثناء (استخدم what())
    // - After the try-catch, always print "Calculation complete"
    
    
    return 0;
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: مترجم C++ عبر الإنترنت