Menu
Coddy logo textTech

טיפול בחריגות ב-OOP

חלק מהיחידה תכנות מונחה עצמים במסלול ה-C++ של Coddy. שיעור 81 מתוך 104.

טיפול בחריגות ב-C++ מספק דרך מובנית לזהות שגיאות זמן ריצה ולהגיב להן. בתכנות מונחה עצמים, חריגות משתלבות באופן טבעי בהיררכיות של מחלקות, ומאפשרות לך ליצור סוגי שגיאות בעלי משמעות, שמכילים הקשר לגבי מה שהשתבש.

המנגנון הבסיסי משתמש בשלוש מילות מפתח: 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++ מספקת היררכיה של מחלקות חריגה, שהבסיס שלה הוא std::exception. מחלקות נגזרות נפוצות כוללות את std::runtime_error עבור בעיות בזמן ריצה ואת std::logic_error עבור טעויות תכנות. כל החריגות התקניות מספקות מתודת what() שמחזירה הודעת שגיאה.

כאשר נזרקת חריגה, מחסנית הקריאות משתחררת — נקראות פונקציות ההרס של כל האובייקטים המקומיים בזמן שהתוכנית מחפשת בלוק catch תואם. כך מובטח ניקוי תקין גם כשמתרחשות שגיאות, וזו הסיבה ש-RAII וטיפול בחריגות עובדים היטב כל כך יחד.

challenge icon

אתגר

קל

בואו נבנה מחשבון בטוח לחלוקה, שמדגים טיפול בחריגות בהקשר מונחה עצמים. תיצרו מחלקה בשם Calculator שתבצע פעולות חילוק ותזרוק חריגות משמעותיות כשמשהו משתבש, ואז תטפלו בחריגות האלה בצורה מסודרת בתוכנית הראשית.

תארגנו את הקוד שלכם בשלושה קבצים:

  • Calculator.h: הגדירו את המחלקה Calculator, שמבצעת פעולות מתמטיות עם טיפול מתאים בשגיאות.

    במחלקה Calculator צריכה להיות מתודה divide(double numerator, double denominator) שמחזירה את תוצאת החילוק. עם זאת, אם המכנה הוא אפס, עליה לזרוק std::runtime_error עם ההודעה Division by zero. אם המונה או המכנה שליליים, עליה לזרוק std::invalid_argument עם ההודעה Negative values not allowed.

    הוסיפו גם מתודה safeSqrt(double value) שמחזירה את השורש הריבועי של הערך. אם הערך שלילי, זרקו std::runtime_error עם ההודעה Cannot compute square root of negative number. תצטרכו לכלול את <cmath> בשביל הפונקציה sqrt.

  • Calculator.cpp: ממשו את המתודות שהוצהרו בקובץ הכותרת. הקפידו לכלול את קובצי הכותרת הדרושים (<stdexcept> ו-<cmath>) ולבדוק את התנאים לפני ביצוע הפעולות.
  • main.cpp: קראו שלוש קלטים (כל אחד בשורה נפרדת):
    1. סוג הפעולה: divide או sqrt
    2. המספר הראשון (double)
    3. המספר השני (double): משמש רק לחלוקה, ומתעלמים ממנו עבור sqrt

    צרו אובייקט Calculator ובצעו את הפעולה המבוקשת בתוך בלוק try. אם הפעולה מצליחה, הדפיסו Result: [value]. אם נתפסת חריגה, הדפיסו Error: [message], כאשר ההודעה מתקבלת מהמתודה what() של החריגה.

    אחרי בלוק ה-try-catch (בלי קשר לשאלה אם התרחשה חריגה), הדפיסו Calculation complete כדי להדגים שהתוכנית ממשיכה לפעול לאחר טיפול בחריגות.

לדוגמה, עם הקלטים divide, 10 ו-2:

Result: 5
Calculation complete

עם הקלטים divide, 10 ו-0:

Error: Division by zero
Calculation complete

עם הקלטים divide, -5 ו-2:

Error: Negative values not allowed
Calculation complete

עם הקלטים sqrt, 16 ו-0:

Result: 4
Calculation complete

עם הקלטים sqrt, -9 ו-0:

Error: Cannot compute square root of negative number
Calculation complete

שימו לב כיצד התוכנית מטפלת בשגיאות בצורה מסודרת באמצעות תפיסת חריגות והצגת הודעות משמעותיות, ואז ממשיכה לפעול. תבנית זו שומרת על טיפול נקי בשגיאות ומאפשרת ללוגיקה הראשית להתמקד במסלול התקין.

נסו בעצמכם

#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 כדי לבצע את הפעולה
    // - אם operation הוא "divide", קראו ל־calc.divide(num1, num2)
    // - אם operation הוא "sqrt", קראו ל־calc.safeSqrt(num1)
    // - אם הפעולה הצליחה, הדפיסו "Result: " ואחריו את התוצאה
    // - תפסו חריגות והדפיסו "Error: " ואחריו את הודעת החריגה (השתמשו ב־what())
    // - אחרי ה־try-catch, הדפיסו תמיד "Calculation complete"
    
    
    return 0;
}
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר C++ אונליין