Menu
Coddy logo textTech

ירושה מרובה

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

ב-C++ מחלקה יכולה לרשת בו-זמנית ממחלקות בסיס מרובות. הדבר נקרא ירושה מרובה ומאפשר לשלב תכונות מהיררכיות שונות של מחלקות.

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

class Printer {
public:
    void print() {
        std::cout << "Printing document" << std::endl;
    }
};

class Scanner {
public:
    void scan() {
        std::cout << "Scanning document" << std::endl;
    }
};

class MultiFunctionDevice : public Printer, public Scanner {
public:
    void copy() {
        scan();
        print();
    }
};

למחלקה MultiFunctionDevice יש כעת גישה למתודות הן של Printer והן של Scanner:

MultiFunctionDevice mfd;
mfd.print();  // מתוך Printer
mfd.scan();   // מתוך Scanner
mfd.copy();   // המתודה שלו עצמו

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

class MultiFunctionDevice : public Printer, public Scanner {
public:
    MultiFunctionDevice() : Printer(), Scanner() {}
};

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

class A { public: void show() {} };
class B { public: void show() {} };
class C : public A, public B {};

C obj;
obj.A::show();  // קרא לגרסה של A
obj.B::show();  // קרא לגרסה של B

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

challenge icon

אתגר

קל

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

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

  • WiFiDevice.h: הגדירו מחלקה בשם WiFiDevice שמייצגת כל מכשיר שמסוגל להתחבר לרשת:
    • איבר מוגן std::string networkName
    • בנאי שמקבל שם רשת ושומר אותו
    • מתודה ציבורית connect() שמדפיסה: Connecting to WiFi: <networkName>
    • מתודה ציבורית status() שמדפיסה: WiFi Status: Connected
  • VoiceControl.h: הגדירו מחלקה בשם VoiceControl שמייצגת כל מכשיר עם יכולות קוליות:
    • איבר מוגן std::string assistantName
    • בנאי שמקבל שם של עוזר קולי ושומר אותו
    • מתודה ציבורית listen() שמדפיסה: <assistantName> is listening...
    • מתודה ציבורית status() שמדפיסה: Voice Status: Active
  • main.cpp: קראו שלוש קלטים (כל אחד בשורה נפרדת):
    1. שם המכשיר (מחרוזת)
    2. שם רשת ה-WiFi (מחרוזת)
    3. שם העוזר הקולי (מחרוזת)

    הגדירו מחלקה בשם SmartSpeaker שיורשת באופן ציבורי גם מ-WiFiDevice וגם מ-VoiceControl:

    • איבר פרטי std::string deviceName
    • בנאי שמקבל את כל שלושת הפרמטרים, מעביר את שם הרשת אל WiFiDevice ואת שם העוזר הקולי אל VoiceControl באמצעות רשימת האתחול, ושומר את שם המכשיר
    • מתודה powerOn() שמדפיסה: <deviceName> powering on..., ואז קוראת ל-connect() ול-listen()
    • מתודה showAllStatus() שמדפיסה את שם המכשיר, ואז משתמשת באופרטור פתרון התחום כדי לקרוא לשתי מתודות status() ולפתור את העמימות

    צרו אובייקט מסוג SmartSpeaker עם ערכי הקלט. קראו ל-powerOn(), הדפיסו שורה ריקה, ואז קראו ל-showAllStatus().

לדוגמה, עם הקלטים Echo, HomeNetwork ו-Alexa:

Echo powering on...
Connecting to WiFi: HomeNetwork
Alexa is listening...

Echo Status:
WiFi Status: Connected
Voice Status: Active

המתודה showAllStatus() צריכה להדפיס את שם המכשיר ואחריו " Status:" בשורה אחת, ואז לקרוא למתודת status() של כל מחלקת בסיס באמצעות WiFiDevice::status() ו-VoiceControl::status(), כדי למנוע עמימות מכיוון שלשתי מחלקות הבסיס יש מתודה באותו שם.

נסו בעצמכם

#include <iostream>
#include <string>
#include "WiFiDevice.h"
#include "VoiceControl.h"

using namespace std;

// TODO: להגדיר מחלקה SmartSpeaker שיורשת באופן ציבורי גם מ-WiFiDevice וגם מ-VoiceControl
// - שדה פרטי: deviceName (string)
// - בנאי: מקבל את deviceName, networkName, assistantName
//   יש להשתמש ברשימת אתחול כדי להעביר את networkName ל-WiFiDevice ואת assistantName ל-VoiceControl
// - המתודה powerOn(): מדפיסה "<deviceName> powering on...", ואז קוראת ל-connect() ול-listen()
// - המתודה showAllStatus(): מדפיסה "<deviceName> Status:", ואז קוראת לשתי המתודות status()
//   באמצעות אופרטור זיהוי התחום (WiFiDevice::status() ו-VoiceControl::status())

class SmartSpeaker {
    // TODO: לממש כאן את המחלקה SmartSpeaker
};

int main() {
    string deviceName, networkName, assistantName;
    getline(cin, deviceName);
    getline(cin, networkName);
    getline(cin, assistantName);

    // TODO: ליצור אובייקט SmartSpeaker עם ערכי הקלט
    // TODO: לקרוא ל-powerOn()
    // TODO: להדפיס שורה ריקה
    // TODO: לקרוא ל-showAllStatus()

    return 0;
}
quiz iconבחנו את עצמכם

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

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

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