Menu
Coddy logo textTech

מחלקות מופשטות

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

מחלקה מופשטת היא כל מחלקה שמכילה לפחות פונקציה וירטואלית טהורה אחת. מכיוון שהממשק שלה אינו שלם, אי אפשר ליצור ישירות אובייקטים של מחלקה מופשטת — היא קיימת אך ורק כדי שמחלקות אחרות יירשו ממנה.

class Shape {
public:
    virtual double area() = 0;      // וירטואלית טהורה - הופכת את Shape למחלקה מופשטת
    virtual double perimeter() = 0; // פונקציה וירטואלית טהורה נוספת
    
    void printInfo() {              // מתודה רגילה - יש לה מימוש
        std::cout << "Area: " << area() << std::endl;
    }
    
    virtual ~Shape() = default;
};

Shape s;  // שגיאה: אי אפשר ליצור מופע של מחלקה מופשטת

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

class Circle : public Shape {
    double radius;
public:
    Circle(double r) : radius(r) {}
    
    double area() override { return 3.14159 * radius * radius; }
    double perimeter() override { return 2 * 3.14159 * radius; }
};

Circle c(5.0);      // OK: Circle מממשת את כל הפונקציות הווירטואליות הטהורות
c.printInfo();      // משתמשת ב-printInfo() שעברה בירושה, וקוראת ל-area() של Circle

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

challenge icon

אתגר

קל

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

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

  • MediaPlayer.h: הגדירו מחלקה מופשטת בשם MediaPlayer שמשמשת כתבנית לכל סוגי המדיה. המחלקה המופשטת שלכם צריכה לכלול:
    • חבר מוגן std::string title
    • חבר מוגן int duration (בשניות)
    • בנאי שמאתחל את שני הערכים
    • מתודה וירטואלית טהורה play(): כל סוג מדיה יתנגן באופן שונה
    • מתודה וירטואלית טהורה getMediaType() שמחזירה std::string
    • מתודה רגילה (לא וירטואלית) showInfo() שמדפיסה: <title> (<duration>s) - Type: <mediaType>. המתודה הזאת צריכה לקרוא ל־getMediaType() כדי לקבל את הסוג
    • הורס וירטואלי
  • Players.h: ממשו שני נגני מדיה קונקרטיים שיורשים מ־MediaPlayer:

    AudioPlayer:

    • חבר פרטי std::string artist
    • בנאי שמקבל כותרת, משך זמן ואמן
    • ממשו את play() כך שתדפיס: Playing audio: <title> by <artist>
    • ממשו את getMediaType() כך שתחזיר "Audio"

    VideoPlayer:

    • חבר פרטי std::string resolution
    • בנאי שמקבל כותרת, משך זמן ורזולוציה
    • ממשו את play() כך שתדפיס: Playing video: <title> in <resolution>
    • ממשו את getMediaType() כך שתחזיר "Video"
  • main.cpp: קראו ארבעה קלטים (כל אחד בשורה נפרדת):
    1. כותרת של רצועת אודיו
    2. משך האודיו (מספר שלם)
    3. כותרת של וידאו
    4. משך הווידאו (מספר שלם)

    צרו AudioPlayer עם האמן "Unknown Artist" ו־VideoPlayer עם הרזולוציה "1080p". שמרו את שניהם במערך של מצביעי MediaPlayer*.

    עברו בלולאה על המערך, ועבור כל נגן, קראו תחילה ל־showInfo() ואז ל־play(). הדפיסו שורה ריקה בין כל פריט מדיה. בסיום, שחררו את האובייקטים שהוקצו דינמית.

לדוגמה, עבור הקלטים Summer Vibes, 180, Nature Documentary ו־3600:

Summer Vibes (180s) - Type: Audio
Playing audio: Summer Vibes by Unknown Artist

Nature Documentary (3600s) - Type: Video
Playing video: Nature Documentary in 1080p

שימו לב שמתודת showInfo() מוגדרת פעם אחת במחלקת הבסיס המופשטת, אבל קוראת למתודה הווירטואלית הטהורה getMediaType(). כך מודגם כיצד מחלקות מופשטות יכולות לשלב מימושים קונקרטיים עם מתודות שהמחלקות הנגזרות חייבות לספק. זכרו להשתמש במילת המפתח override בכל המתודות שדורכות על מתודות קיימות.

נסו בעצמכם

#include <iostream>
#include <string>
#include "MediaPlayer.h"
#include "Players.h"

int main() {
    // קרא את הקלט
    std::string audioTitle;
    int audioDuration;
    std::string videoTitle;
    int videoDuration;
    
    std::getline(std::cin, audioTitle);
    std::cin >> audioDuration;
    std::cin.ignore();
    std::getline(std::cin, videoTitle);
    std::cin >> videoDuration;
    
    // TODO: צור AudioPlayer עם האמן "Unknown Artist"
    
    // TODO: צור VideoPlayer עם רזולוציה "1080p"
    
    // TODO: אחסן את שניהם במערך של מצביעי MediaPlayer*
    
    // TODO: עבור על המערך ועבור כל נגן:
    //       1. קרא ל-showInfo()
    //       2. קרא ל-play()
    //       3. הדפס שורה ריקה בין כל פריט מדיה
    
    // TODO: שחרר את האובייקטים שהוקצו דינמית
    
    return 0;
}
quiz iconבחנו את עצמכם

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

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

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