Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

الكلمة المفتاحية 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 داخل الدالة const getData() إلى حدوث خطأ في المصرّف. تخبر الكلمة المفتاحية mutable المصرّفَ بما يلي: "يُسمح بتغيير هذا العضو حتى عندما يُعتبَر الكائن const."

تشمل حالات الاستخدام الشائعة للقابلية للتغيير تخزين القيم المحسوبة مؤقتًا، وتتبع إحصاءات الوصول، وبدائيات المزامنة مثل كائنات mutex. في كل حالة، لا يغيّر التعديل ما يمثّله الكائن منطقيًا؛ فالقيمة المخزنة مؤقتًا ليست سوى تحسين، وعداد الوصول عبارة عن بيانات وصفية حول الاستخدام.

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
    • أسلوب getTitle() من نوع const يعيد العنوان
    • أسلوب getContent() من نوع const يزيد قيمة viewCount (وهو مسموح لأن المتغير mutable) ويعيد المحتوى
    • أسلوب getViewCount() من نوع const يعيد عدد مرات الوصول إلى المحتوى
    • أسلوب getPreview() من نوع const يعيد أول 20 محرفًا من المحتوى متبوعةً بـ "...". يجب ألا يزيد هذا الأسلوب عدد مرات العرض، لأنه مجرد معاينة
  • main.cpp: وضّح كيف تسمح mutable بتتبع إحصائيات الوصول مع إبقاء الأساليب ثابتة منطقيًا. اقرأ عنوان المستند ومحتواه من الإدخال (كل منهما في سطر منفصل)، ثم:
    • أنشئ كائن 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: Print "Views after preview: <count>"
    
    // TODO: الوصول إلى المحتوى الكامل واطبع "Content: <content>"
    
    // TODO: Print "Views after first read: <count>"
    
    // TODO: الوصول إلى المحتوى مرة أخرى واطبع "Content: <content>"
    
    // TODO: Print "Views after second read: <count>"
    
    // TODO: استدعِ displayDocument() مع المستند الخاص بك
    
    // TODO: Print "Final view count: <count>"
    
    return 0;
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: مترجم C++ عبر الإنترنت