רמות גישה בירושה
חלק מהיחידה תכנות מונחה עצמים במסלול ה-C++ של Coddy. שיעור 49 מתוך 104.
סוג הירושה שתבחר משפיע על האופן שבו ניתן לגשת לחברי מחלקת הבסיס במחלקה הנגזרת. ב-C++ יש שלוש אפשרויות: ירושה public, ירושה protected וירושה private.
כך כל סוג משנה את הגישה לחברים:
| חבר במחלקת הבסיס | ירושה public | ירושה protected | ירושה private |
|---|---|---|---|
| public | public | protected | private |
| protected | protected | protected | private |
| private | לא נגיש | לא נגיש | לא נגיש |
class Base {
public:
int pub;
protected:
int prot;
private:
int priv;
};
class PublicDerived : public Base {
// pub הוא ציבורי, prot הוא מוגן, priv אינו נגיש
};
class ProtectedDerived : protected Base {
// pub הוא מוגן, prot הוא מוגן, priv אינו נגיש
};
class PrivateDerived : private Base {
// pub הוא פרטי, prot הוא פרטי, priv אינו נגיש
};ירושה ציבורית מדגמלת קשרי "הוא-סוג-של", והיא הנפוצה ביותר בפער ניכר. Dog הוא Animal, ולכן קוד חיצוני יכול להתייחס אליו ככזה.
ירושה מוגנת ופרטית מדגימות קשרי "ממומש באמצעות". הן מסתירות את הממשק של מחלקת הבסיס מקוד חיצוני, וזה שימושי כשרוצים לעשות שימוש חוזר במימוש בלי לחשוף את קשר הירושה. בירושה פרטית, גם מחלקות שיורשות בהמשך מאבדות גישה לחברי מחלקת הבסיס המקורית.
אתגר
קלנבחן כיצד סוגי ירושה שונים משפיעים על הנגישות לחברים, באמצעות בניית היררכיית מכשירים פשוטה. תיצור מחלקת בסיס Device ושלוש מחלקות נגזרות, שכל אחת משתמשת בסוג ירושה שונה, כדי לראות בעצמך כיצד ירושה ציבורית, מוגנת ופרטית משנה את הגישה לחברים.
תארגן את הקוד שלך בשני קבצים:
Device.h: הגדר מחלקת בסיסDeviceעם שלושה חברים ברמות גישה שונות:std::string modelציבוריint serialNumberמוגןint internalCodeפרטי (מוגדר כברירת מחדל ל־999)- מתודה ציבורית
showModel()שמדפיסה:Model: <model>
לאחר מכן, צור שלוש מחלקות נגזרות, שכל אחת יורשת מ־
Deviceבאמצעות סוג ירושה שונה:PublicDeviceעם ירושה ציבורית: הוסף מתודהdisplaySerial()שמדפיסה:Serial: <serialNumber>ProtectedDeviceעם ירושה מוגנת: הוסף מתודהdisplaySerial()שמדפיסה:Serial: <serialNumber>PrivateDeviceעם ירושה פרטית: הוסף מתודהdisplaySerial()שמדפיסה:Serial: <serialNumber>
לכל מחלקה נגזרת צריכה להיות בנאית שמקבלת שם דגם ומספר סידורי, מעבירה את הדגם למחלקת הבסיס ומגדירה את המספר הסידורי.
main.cpp: קרא שתי קלטים (כל אחד בשורה נפרדת):- שם הדגם (מחרוזת)
- מספר סידורי (מספר שלם)
צור אובייקט אחד מכל סוג נגזר, עם אותם ערכי קלט. לאחר מכן הדגם את הבדלי הגישה:
עבור
PublicDevice:- קרא ישירות ל־
showModel()באובייקט (הפעולה מצליחה כי ירושה ציבורית משאירה אותו ציבורי) - גש ישירות אל
modelוהדפס:Direct model access: <model> - קרא ל־
displaySerial()
עבור
ProtectedDeviceו־PrivateDevice:- קרא רק ל־
displaySerial()(אי אפשר עוד לגשת מבחוץ באופן ציבורי אלshowModel()ואלmodelשעברו בירושה)
פורמט הפלט:
--- PublicDevice --- Model: <model> Direct model access: <model> Serial: <serial> --- ProtectedDevice --- Serial: <serial> --- PrivateDevice --- Serial: <serial>
לדוגמה, עם הקלטים Laptop ו־12345:
--- PublicDevice ---
Model: Laptop
Direct model access: Laptop
Serial: 12345
--- ProtectedDevice ---
Serial: 12345
--- PrivateDevice ---
Serial: 12345שים לב שרק PublicDevice מאפשרת גישה מבחוץ לחברים הציבוריים שעברו בירושה. סוגי הירושה האחרים מסתירים את הממשק של מחלקת הבסיס מקוד חיצוני, אך המחלקות הנגזרות עדיין יכולות לגשת לחברים מוגנים מבפנים, באמצעות המתודות שלהן.
נסו בעצמכם
#include <iostream>
#include <string>
#include "Device.h"
using namespace std;
int main() {
// קרא את הקלט
string modelName;
int serialNum;
getline(cin, modelName);
cin >> serialNum;
// TODO: צור אובייקטים מכל טיפוס נגזר באמצעות ערכי הקלט
// PublicDevice pubDev(modelName, serialNum);
// ProtectedDevice protDev(modelName, serialNum);
// PrivateDevice privDev(modelName, serialNum);
// TODO: הדגם גישה ל-PublicDevice
cout << "--- PublicDevice ---" << endl;
// - קרא ל-showModel() ישירות על האובייקט
// - גש ישירות ל-model והדפס: Direct model access: <model>
// - קרא ל-displaySerial()
// TODO: הדגם גישה ל-ProtectedDevice
cout << "--- ProtectedDevice ---" << endl;
// - קרא רק ל-displaySerial() (הגישה ל-showModel ול-model אינה ציבורית)
// TODO: הדגם גישה ל-PrivateDevice
cout << "--- PrivateDevice ---" << endl;
// - קרא רק ל-displaySerial() (הגישה ל-showModel ול-model אינה ציבורית)
return 0;
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות OOP
קבצים חיצונייםבנייה וקומפילציה של C++קובצי Header וקובצי מקורמרחבי שמות והיקףמבוא ל-OOP ב-C++מחלקות לעומת אובייקטיםמצביע thisמתודות (פונקציות איבר)תכונות (חברי נתונים)יסודות הבנאים וההורסיםסיכום – מחשבון פשוט4מאפייני מחלקה
חברי מופע לעומת חברים סטטייםגטרים וסטריםפונקציות איבר קבועותמילת המפתח mutableמתודות ומשתנים סטטייםפונקציות ומחלקות ידידותסיכום – מנהל חשבונות בנק7ירושה
ירושה בסיסיתרמות גישה בירושהסדר הקריאות לבנאי ולמפרקדריסת מתודותפונקציות וירטואליות ו־VTableירושה מרובהירושה וירטואליתסיכום – היררכיית עובדים2ניהול זיכרון
זיכרון Stack לעומת Heapמצביעים והפניותזיכרון דינמי (new/delete)מצביעים חכמים ב-C++RAII ב-C++סיכום – מנהל מערכים דינמיים5כימוס
מצייני גישה ב-C++מצייני גישה לעומקהסתרת מידעמבנה לעומת מחלקהמחלקות מקוננות ופנימיותסיכום – מערכת רשומות תלמידים8רב־צורתיות
רב־צורתיות בזמן קומפילציה לעומת בזמן ריצההעמסת פונקציותפונקציות וירטואליות — מבט נוסףפונקציות וירטואליות טהורותמחלקות מופשטותתכנון ממשקים ב־C++המרה דינמית ו־RTTIסיכום — מחשבון צורות3בנאים והורסים
בנאי ברירת מחדלבנאי עם פרמטריםבנאי העתקהבנאי העברהרשימות אתחול בבנאיםבנאים מאציליםמעמיקים בנושא הורסיםכלל השלושה / החמישה / האפססיכום – מחלקת String6העמסת אופרטורים
מבוא להעמסת אופרטוריםהעמסת אופרטורים אריתמטייםהעמסת אופרטורי השוואהאופרטורי זרםהעמסת אופרטור השמההעמסת האופרטורים [] ו־()אופרטורים להמרת טיפוסיםסיכום – מחלקת Matrix9תבניות
תבניות פונקציהתבניות מחלקההתמחות תבניותתבניות ארגומנטים משתנותיסודות SFINAE ותכונות טיפוסיםסיכום – מכל גנריתרגלו בעצמכם: קומפיילר C++ אונליין