פונקציות איבר קבועות
חלק מהיחידה תכנות מונחה עצמים במסלול ה-C++ של Coddy. שיעור 29 מתוך 104.
ראית את מילת המפתח const בשיטות getter בשיעור הקודם. אבל מה בדיוק המשמעות של הופעת const אחרי רשימת הפרמטרים של פונקציה? פונקציית חברה const מתחייבת לא לשנות אף אחד מהמשתנים החברים של האובייקט.
class Rectangle {
int width;
int height;
public:
Rectangle(int w, int h) : width(w), height(h) {}
int getArea() const { // פונקציית חבר מסוג const
return width * height; // מותר לקרוא את שדות המחלקה
}
void setWidth(int w) { // לא const - משנה את האובייקט
width = w;
}
};ה-const שמופיע אחרי רשימת הפרמטרים אומר לקומפיילר: "הפונקציה הזו לא תשנה את מצב האובייקט." אם תנסה לשנות משתנה חבר בתוך פונקציית const, הקומפיילר יפיק שגיאה.
הדבר נעשה חיוני כשעובדים עם אובייקטים מסוג const או עם הפניות מסוג const. אובייקט מסוג const יכול לקרוא רק לפונקציות איבר מסוג const:
void printArea(const Rectangle& rect) {
std::cout << rect.getArea(); // OK - getArea() היא פונקציית const
// rect.setWidth(10); // ERROR - setWidth() אינה פונקציית const
}סימון פונקציות כ-const כשהן לא משנות את האובייקט הוא נוהג טוב. הוא מתעד את הכוונה שלך, מאפשר לפונקציה לעבוד עם אובייקטים מסוג const ועוזר למהדר לזהות שינויים מקריים. יש לסמן const כל פונקציית איבר שרק קוראת נתונים.
אתגר
קלבואו נבנה ממיר טמפרטורות שמדגים מתי ולמה כדאי להשתמש בפונקציות איבר מסוג const. תיצרו מחלקה שבה חלק מהמתודות רק קוראות נתונים (ולכן צריכות להיות const), בעוד אחרות משנות את מצב האובייקט.
תיצרו שני קבצים כדי לארגן את הקוד:
Temperature.h: הגדירו מחלקה בשםTemperatureששומרת ערך טמפרטורה ומספקת דרכים שונות לקרוא אותו ולשנות אותו. המחלקה שלכם צריכה לכלול:- איבר פרטי
celsius(double) לשמירת הטמפרטורה - בנאי שמקבל ערך התחלתי במעלות צלזיוס
- מתודת
getCelsius()שמחזירה את הערך השמור. היא צריכה להיות const, מכיוון שהיא רק קוראת נתונים - מתודת
getFahrenheit()שמחשבת ומחזירה את הטמפרטורה במעלות פרנהייט באמצעות הנוסחהcelsius * 9.0 / 5.0 + 32.0, וגם היא const מכיוון שהיא אינה משנה דבר - מתודת
getKelvin()שמחזירה את הטמפרטורה בקלווין באמצעותcelsius + 273.15: גם היא צריכה להיות const - מתודת
setCelsius(double value)שמעדכנת את הטמפרטורה השמורה. היא לא יכולה להיות const מכיוון שהיא משנה את האובייקט - מתודת
adjustBy(double delta)שמוסיפה את ההפרש לטמפרטורה הנוכחית; גם היא לא const
- איבר פרטי
main.cpp: הדגימו כיצד פועלות פונקציות איבר מסוג const עם אובייקטים רגילים וגם עם אובייקטים מסוג const. קראו ערך טמפרטורה התחלתי מהקלט, ואז:- צרו אובייקט
Temperatureעם ערך הקלט - הדפיסו
"Initial: <celsius>C = <fahrenheit>F = <kelvin>K" - כווננו את הטמפרטורה ב-
10.0מעלות - הדפיסו
"After adjustment: <celsius>C" - צרו פונקציית עזר
void printReadings(const Temperature& temp)שמקבלת הפניה קבועה ומדפיסה"Reading: <celsius>C, <fahrenheit>F". הפונקציה הזאת יכולה לקרוא רק למתודות const של temp - קראו ל-
printReadings()עם אובייקט הטמפרטורה שלכם - קבעו את הטמפרטורה ל-
0.0(נקודת הקיפאון) - הדפיסו
"Freezing point: <celsius>C = <fahrenheit>F"
- צרו אובייקט
עצבו את כל ערכי הטמפרטורה עם ספרה אחת אחרי הנקודה העשרונית באמצעות std::fixed ו-std::setprecision(1) מתוך <iomanip>.
התובנה המרכזית כאן היא ש-printReadings() מקבלת הפניה קבועה, ולכן היא יכולה לקרוא רק למתודות שמסומנות כ-const. לכן חשוב לסמן כראוי את מתודות האחזור שלכם כ-const. כך הן יכולות לפעול בהקשרים שבהם אי אפשר לשנות את האובייקט.
נסו בעצמכם
#include <iostream>
#include <iomanip>
#include "Temperature.h"
using namespace std;
// TODO: ממשו כאן את המתודות של המחלקה Temperature
// בנאי
Temperature::Temperature(double initialCelsius) {
// TODO: אתחלו את celsius
}
// TODO: ממשו את getCelsius() כמתודה מסוג const
// TODO: ממשו את getFahrenheit() כמתודה מסוג const
// TODO: ממשו את getKelvin() כמתודה מסוג const
// TODO: ממשו את setCelsius(double value)
// TODO: ממשו את adjustBy(double delta)
// TODO: צרו פונקציית עזר printReadings שמקבלת const Temperature&
// ומדפיסה "Reading: <celsius>C, <fahrenheit>F"
// הערה: פונקציה זו יכולה לקרוא רק למתודות מסוג const של temp!
int main() {
double initialTemp;
cin >> initialTemp;
// הגדירו את תבנית הפלט
cout << fixed << setprecision(1);
// TODO: צור אובייקט Temperature עם ערך הקלט
// TODO: הדפס "Initial: <celsius>C = <fahrenheit>F = <kelvin>K"
// TODO: שנה את הטמפרטורה ב-10.0 מעלות
// TODO: הדפס "After adjustment: <celsius>C"
// TODO: קרא ל-printReadings() עם אובייקט הטמפרטורה שלך
// TODO: הגדר את הטמפרטורה ל-0.0 (נקודת הקיפאון)
// TODO: הדפס "Freezing point: <celsius>C = <fahrenheit>F"
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++ אונליין