Menu
Coddy logo textTech

מילת המפתח mutable

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

למדת שפונקציות איבר const אינן יכולות לשנות משתני איבר כלשהם. אבל לפעמים צריך לשנות איבר שאינו משפיע על המצב הלוגי של האובייקט — כמו מטמון או מונה גישות. מילת המפתח mutable מספקת חריג להגבלת const.

ניתן לשנות איבר mutable אפילו בתוך פונקציית איבר const:

class DataFetcher {
    std::string data;
    mutable int accessCount;    // ניתן לשנות בפונקציות const
    
public:
    DataFetcher(std::string d) : data(d), accessCount(0) {}
    
    std::string getData() const {
        accessCount++;          // מותר כי accessCount הוא mutable
        return data;
    }
    
    int getAccessCount() const {
        return accessCount;
    }
};

ללא mutable, הגדלת accessCount בתוך הפונקציה הקבועה getData() תגרום לשגיאת קומפילציה. מילת המפתח mutable אומרת למהדר: "מותר לשנות את האיבר הזה גם כשהאובייקט נחשב קבוע."

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

class Circle {
    double radius;
    mutable double cachedArea;
    mutable bool areaCached;
    
public:
    double getArea() const {
        if (!areaCached) {
            cachedArea = 3.14159 * radius * radius;
            areaCached = true;
        }
        return cachedArea;
    }
};

השתמשו ב־mutable במשורה — יש להחיל אותו רק על איברים שאינם משפיעים על המצב הנצפה של האובייקט.

challenge icon

אתגר

קל

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

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

  • Document.h: הגדירו מחלקה בשם Document ששומרת תוכן ועוקבת בחשאי אחר נתוני הצפייה. המחלקה שלכם צריכה לכלול:
    • חברים פרטיים: title (מחרוזת), content (מחרוזת), ו-mutable int viewCount שאפשר לשנות אפילו במתודות const
    • בנאי שמקבל כותרת ותוכן ומאתחל את viewCount ל-0
    • מתודת const בשם getTitle() שמחזירה את הכותרת
    • מתודת const בשם getContent() שמגדילה את viewCount (הדבר מותר כי הוא mutable) ומחזירה את התוכן
    • מתודת const בשם getViewCount() שמחזירה את מספר הפעמים שבהן ניגשו לתוכן
    • מתודת const בשם getPreview() שמחזירה את 20 התווים הראשונים של התוכן ואחריהם "...". מונה הצפיות לא אמור לגדול בעקבות זאת, כי מדובר בתצוגה מקדימה בלבד
  • main.cpp: הדגימו כיצד mutable מאפשר לעקוב אחר נתוני גישה תוך שמירה על כך שהמתודות יהיו const מבחינה לוגית. קראו מהקלט כותרת ותוכן של מסמך (כל אחד בשורה נפרדת), ואז:
    • צרו Document עם ערכי הקלט
    • הדפיסו "Title: <title>"
    • הדפיסו "Preview: <preview>"
    • הדפיסו "Views after preview: <count>"
    • גשו לתוכן המלא באמצעות getContent() והדפיסו "Content: <content>"
    • הדפיסו "Views after first read: <count>"
    • גשו שוב לתוכן והדפיסו "Content: <content>"
    • הדפיסו "Views after second read: <count>"
    • צרו פונקציית עזר void displayDocument(const Document& doc) שמדפיסה "[Const Access] <title>: <content>". כך תוכלו להוכיח שמתודות ה-const שלנו פועלות עם הפניות const
    • קראו ל-displayDocument() עם המסמך שלכם
    • הדפיסו "Final view count: <count>"

מספר הצפיות צריך להיות 0 אחרי התצוגה המקדימה (מכיוון שתצוגות מקדימות אינן נחשבות לצפיות מלאות), ואז לגדול בכל פעם שקוראים ל-getContent(). מילת המפתח mutable מאפשרת זאת; בלעדיה, לא הייתם יכולים לשנות את viewCount בתוך מתודות const כמו getContent().

אם התוכן קצר מ-20 תווים, getPreview() צריכה להחזיר את התוכן כולו ואחריו "...".

נסו בעצמכם

#include <iostream>
#include <string>
#include "Document.h"
using namespace std;

// TODO: לממש את הפונקציה displayDocument
// היא צריכה להדפיס: "[Const Access] <title>: <content>"
void displayDocument(const Document& doc) {
    // TODO: לממש את הפונקציה הזו
}

int main() {
    // קריאת קלט
    string title;
    string content;
    getline(cin, title);
    getline(cin, content);
    
    // TODO: ליצור Document עם ערכי הקלט
    
    // TODO: להדפיס "Title: <title>"
    
    // TODO: להדפיס "Preview: <preview>"
    
    // TODO: להדפיס "Views after preview: <count>"
    
    // TODO: לגשת לתוכן המלא ולהדפיס "Content: <content>"
    
    // TODO: להדפיס "Views after first read: <count>"
    
    // TODO: לגשת שוב לתוכן ולהדפיס "Content: <content>"
    
    // TODO: להדפיס "Views after second read: <count>"
    
    // TODO: לקרוא ל־displayDocument() עם המסמך שלך
    
    // TODO: להדפיס "Final view count: <count>"
    
    return 0;
}
quiz iconבחנו את עצמכם

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

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

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