הסתרת מידע
חלק מהיחידה תכנות מונחה עצמים במסלול ה-C++ של Coddy. שיעור 36 מתוך 104.
הסתרת מידע היא יותר מסתם שימוש במצייני גישה. זהו עקרון תכנון שבו מסתירים לא רק נתונים, אלא גם את פרטי המימוש של אופן הפעולה הפנימי של המחלקה.
המטרה היא לחשוף רק את מה שמשתמשי המחלקה שלך צריכים לדעת, ולהסתיר את כל השאר. כך אפשר לשנות את המימוש הפנימי בלי להשפיע על קוד שמשתמש במחלקה שלך:
class Temperature {
double kelvin; // ייצוג פנימי - פרט מוסתר
public:
void setCelsius(double c) {
kelvin = c + 273.15; // ההמרה מוסתרת מהמשתמש
}
double getCelsius() const {
return kelvin - 273.15;
}
double getFahrenheit() const {
return (kelvin - 273.15) * 9.0/5.0 + 32;
}
};המשתמשים במחלקה הזו לא יודעים (וגם לא צריכים לדעת) שהטמפרטורה נשמרת פנימית בקלווין. אם תחליטו מאוחר יותר לשמור אותה במקום זאת בצלזיוס, תשנו רק את המימוש הפרטי — הממשק הציבורי יישאר ללא שינוי.
ההפרדה הזאת בין הממשק למימוש מספקת כמה יתרונות: היא מפחיתה את המורכבות עבור המשתמשים במחלקה שלך, מונעת שימוש שגוי ולא מכוון בנתונים פנימיים והופכת את הקוד שלך לקל יותר לתחזוקה. כשהפרטים הפנימיים מוסתרים, אפשר לבצע אופטימיזציה או לשנות את המבנה בלי לשבור קוד קיים שתלוי במחלקה שלך.
אתגר
קלבואו נבנה מחלקת Distance שמדגימה הסתרת מידע באמצעות אחסון פנימי של מרחקים בסנטימטרים, תוך מתן ממשק פשוט לעבודה עם יחידות שונות. משתמשים במחלקה שלך לא יצטרכו לדעת (או לדאוג) איך המרחק מאוחסן באופן פנימי: הם פשוט יעבדו עם היחידה הנוחה להם.
תיצור שני קבצים כדי לארגן את הקוד שלך:
Distance.h: הגדר מחלקתDistanceשמסתירה את הייצוג הפנימי שלה, ובו בזמן חושפת ממשק ציבורי גמיש. אחסן את המרחק באופן פנימי בסנטימטרים (בתורdouble). זהו פרט המימוש המוסתר שלך. הממשק הציבורי שלך צריך לאפשר למשתמשים:- להגדיר את המרחק באמצעות
setMeters(double m),setCentimeters(double cm)אוsetKilometers(double km) - לקבל את המרחק באמצעות
getMeters(),getCentimeters()אוgetKilometers(): כולן צריכות להיות מתודות const - לחבר שני מרחקים באמצעות
add(const Distance& other), שמחזירה אובייקטDistanceחדש
כלול בנאי ברירת מחדל שמאתחל את המרחק לאפס. לוגיקת ההמרה צריכה להיות מוסתרת בתוך המתודות שלך: המשתמשים פשוט קוראים ל-
setKilometers(5)ול-getMeters()בלי לדאוג לפורמט האחסון הפנימי.- להגדיר את המרחק באמצעות
main.cpp: הדגם כיצד משתמשים יכולים לעבוד עם המחלקה שלך בלי לדעת מהו הייצוג הפנימי. קרא מהקלט שני ערכי מרחק: הראשון במטרים והשני בקילומטרים. לאחר מכן:- צור אובייקט
Distanceוהגדר אותו באמצעות הערך במטרים - הדפס
"In meters: <value> m" - הדפס
"In centimeters: <value> cm" - הדפס
"In kilometers: <value> km" - צור אובייקט
Distanceשני והגדר אותו באמצעות הערך בקילומטרים - חבר את שני המרחקים ושמור את התוצאה
- הדפס
"Combined in meters: <value> m"
- צור אובייקט
היופי בהסתרת מידע הוא שבהמשך תוכל לשנות את האחסון הפנימי מסנטימטרים למילימטרים (או לכל יחידה אחרת) בלי לשנות את האופן שבו המשתמשים עובדים עם המחלקה שלך. הממשק הציבורי נשאר יציב גם אם המימוש משתנה.
עצב את כל ערכי הפלט עם שתי ספרות אחרי הנקודה העשרונית באמצעות std::fixed ו-std::setprecision(2) מתוך <iomanip>. המר את מחרוזות הקלט למספרים מסוג double באמצעות std::stod().
המרות ייחוס: 1 מטר = 100 סנטימטרים, 1 קילומטר = 1000 מטרים.
נסו בעצמכם
#include <iostream>
#include <string>
#include <iomanip>
#include "Distance.h"
using namespace std;
int main() {
// קרא את ערכי הקלט
string metersInput, kmInput;
cin >> metersInput; // הערך הראשון במטרים
cin >> kmInput; // הערך השני בקילומטרים
// המר מחרוזות לערכים מסוג double באמצעות std::stod()
double metersValue = stod(metersInput);
double kmValue = stod(kmInput);
// הגדר את עיצוב הפלט
cout << fixed << setprecision(2);
// TODO: צור אובייקט Distance ראשון והגדר אותו באמצעות הערך במטרים
// TODO: הדפס "In meters: <value> m"
// TODO: הדפס "In centimeters: <value> cm"
// TODO: הדפס "In kilometers: <value> km"
// TODO: צור אובייקט Distance שני והגדר אותו באמצעות הערך בקילומטרים
// TODO: חבר את שני המרחקים באמצעות המתודה add()
// TODO: הדפס "Combined in meters: <value> m"
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++ אונליין