מצייני גישה לעומק
חלק מהיחידה תכנות מונחה עצמים במסלול ה-C++ של Coddy. שיעור 35 מתוך 104.
כעת, לאחר שהבנת את שלושת מצייני הגישה, נבדוק איך הם מתנהגים כשיש לך כמה אובייקטים מאותה מחלקה. תפיסה שגויה נפוצה היא ש־private פירושו "רק האובייקט הזה" — אבל למעשה פירושו "רק המחלקה הזאת".
אובייקטים מאותה מחלקה יכולים לגשת לאיברים הפרטיים זה של זה:
class Person {
std::string secret;
public:
Person(std::string s) : secret(s) {}
void readSecret(const Person& other) {
// גישה לשדה הפרטי של אובייקט אחר - מותרת!
std::cout << other.secret;
}
};זה עובד משום שבקרת הגישה נאכפת ברמת המחלקה, ולא ברמת האובייקט. המתודה readSecret היא חלק מהמחלקה Person, ולכן היא יכולה לגשת לאיברים פרטיים של כל אובייקט מסוג Person. התכנון הזה מאפשר לפעולות כמו בנאים להעתקה ואופרטורי השוואה לעבוד עם המצב הפנימי של אובייקטים אחרים.
אפשר גם לעבור בין מצייני גישה כמה פעמים בתוך מחלקה:
class Mixed {
public:
void publicMethod1();
private:
int privateData;
public:
void publicMethod2(); // חזרה ל-public
private:
void privateHelper(); // שוב private
};אף שזו כתיבה תקינה ב־C++, בדרך כלל עדיף לקבץ את החברים לפי רמת הגישה לשם קריאות. רוב מדריכי הסגנון ממליצים לרשום תחילה את החברים הציבוריים (מכיוון שהם מהווים את הממשק של המחלקה), אחריהם את המוגנים ואז את הפרטיים.
אתגר
קלבואו נבנה מערכת להשוואת ארנקים שמדגימה כיצד אובייקטים מאותה מחלקה יכולים לגשת לחברים הפרטיים זה של זה. זוהי תובנה מרכזית לגבי בקרת הגישה ב-C++: היא נאכפת ברמת המחלקה, ולא ברמת האובייקט.
תיצרו שני קבצים כדי לארגן את הקוד:
Wallet.h: הגדירו מחלקתWalletששומרת נתונים פיננסיים פרטיים, אך מאפשרת השוואות בין ארנקים. המחלקה שלכם צריכה לכלול:- חברים פרטיים:
ownerName(מחרוזת) ו-balance(מספר ממשי) - בנאי שמקבל את שם הבעלים ואת היתרה ההתחלתית
- פונקציית getter ציבורית
getOwnerName()שמחזירה את שם הבעלים - מתודה
hasMoreThan(const Wallet& other)שמשווה בין היתרה של הארנק הזה לבין היתרה הפרטית של ארנק אחר, ומחזירהtrueאם יש בארנק הזה יותר כסף - מתודה
combinedBalance(const Wallet& other)שמחזירה את סכום היתרות הפרטיות של שני הארנקים - מתודה
transferTo(Wallet& other, double amount)שמעבירה כסף מהארנק הזה לארנק אחר. עליה לשנות ישירות את היתרות הפרטיות של שני הארנקים (רק אם הסכום חיובי ואינו עולה על היתרה של הארנק הזה)
- חברים פרטיים:
main.cpp: הדגימו גישה לחברים פרטיים של אובייקטים אחרים. קראו מהקלט שני שמות בעלים ושתי יתרות (ארבע שורות: name1, balance1, name2, balance2), ואז:- צרו שני אובייקטים מסוג
Walletעם ערכי הקלט - הדפיסו
"Combined wealth: $<amount>"באמצעותcombinedBalance() - השוו בין הארנקים והדפיסו
"<name> has more money"עבור הבעלים שהיתרה שלו גבוהה יותר (השתמשו ב-hasMoreThan()כדי לקבוע זאת) - העבירו
100.0מהארנק הראשון לשני באמצעותtransferTo() - הדפיסו
"After transfer:" - הדפיסו שוב
"Combined wealth: $<amount>"(הסכום אמור להישאר ללא שינוי) - השוו שוב והדפיסו
"<name> has more money"(ייתכן שהתוצאה השתנתה)
- צרו שני אובייקטים מסוג
המושג המרכזי כאן הוא שמתודות כמו hasMoreThan() ו-transferTo() יכולות לגשת ישירות לחבר הפרטי balance של אובייקט Wallet אחר: אין צורך בפונקציות getter. הדבר אפשרי מכיוון שבקרת הגישה ב-C++ מבוססת על מחלקות: כל קוד בתוך מחלקת Wallet יכול לגשת לחברים הפרטיים של כל אובייקט מסוג Wallet.
עצבו את כל הערכים הכספיים עם שתי ספרות אחרי הנקודה העשרונית באמצעות std::fixed ו-std::setprecision(2) מתוך <iomanip>. המירו את ערכי היתרה שהתקבלו בקלט באמצעות std::stod().
נסו בעצמכם
#include <iostream>
#include <string>
#include <iomanip>
#include "Wallet.h"
using namespace std;
int main() {
// קרא את הקלט: name1, balance1, name2, balance2
string name1, name2;
string balanceStr1, balanceStr2;
getline(cin, name1);
getline(cin, balanceStr1);
getline(cin, name2);
getline(cin, balanceStr2);
double balance1 = stod(balanceStr1);
double balance2 = stod(balanceStr2);
// הגדר את עיצוב הפלט לערכים כספיים
cout << fixed << setprecision(2);
// TODO: צור שני אובייקטים מסוג Wallet עם ערכי הקלט
// TODO: הדפס את ההון המשולב באמצעות combinedBalance()
// פורמט: "Combined wealth: $<amount>"
// TODO: השווה בין הארנקים באמצעות hasMoreThan() והדפס למי יש יותר כסף
// פורמט: "<name> has more money"
// TODO: העבר 100.0 מהארנק הראשון לשני באמצעות transferTo()
// TODO: הדפס "After transfer:"
// TODO: הדפס שוב את ההון המשולב
// TODO: השווה שוב והדפס למי יש יותר כסף
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++ אונליין