c++ - क्या 'mutable' कीवर्ड का कोई उद्देश्य किसी अन्य उद्देश्य को किसी कॉन्स्ट फ़ंक्शन द्वारा संशोधित करने की अनुमति देने के अलावा है?




keyword (12)

कुछ समय पहले मैं कुछ कोड में आया था जो एक mutable कीवर्ड वाले वर्ग के सदस्य चर को चिह्नित करता था। जहां तक ​​मैं इसे देख सकता हूं बस आपको एक const विधि में एक चर को संशोधित करने की अनुमति देता है:

class Foo  
{  
private:  
    mutable bool done_;  
public:  
    void doSomething() const { ...; done_ = true; }  
};

क्या यह इस कीवर्ड का एकमात्र उपयोग है या आंखों की तुलना में इसके लिए और भी कुछ है? मैंने तब से इस तकनीक का उपयोग कक्षा में किया है, जो boost::mutex को boost::mutex रूप में चिह्नित करने के लिए const फ़ंक्शंस को थ्रेड-सुरक्षा कारणों से लॉक करने की इजाजत देता है, लेकिन, ईमानदार होने के लिए, यह एक हैक की तरह लगता है।


"म्यूटेबल" का उपयोग करें जब उन चीज़ों के लिए जो उपयोगकर्ता के लिए स्थिर हैं (और इस प्रकार सार्वजनिक वर्ग 'एपीआई में "कॉन्स्ट" गेटर्स होना चाहिए) लेकिन अंतर्निहित कार्यान्वयन (आपके .cpp में कोड) में स्टेटलेस नहीं हैं।

जिन मामलों का मैं इसे अक्सर उपयोग करता हूं वे राज्य-कम "सादे पुराने डेटा" सदस्यों के आलसी प्रारंभिक हैं। अर्थात्, यह संकीर्ण मामलों में आदर्श है जब ऐसे सदस्य या तो निर्माण (प्रोसेसर) या आसपास के (मेमोरी) के लिए महंगा होते हैं और ऑब्जेक्ट के कई उपयोगकर्ता कभी भी उनके लिए नहीं पूछेंगे। उस स्थिति में आप प्रदर्शन के लिए बैक एंड पर आलसी निर्माण चाहते हैं, क्योंकि 90% ऑब्जेक्ट्स को कभी भी उन्हें बनाने की आवश्यकता नहीं होगी, फिर भी आपको सार्वजनिक खपत के लिए सही स्टेटलेस एपीआई पेश करने की आवश्यकता है।


Boost :: mutex के साथ आपका उपयोग बिल्कुल वही है जो इस कीवर्ड का इरादा है। एक और उपयोग आंतरिक परिणाम कैशिंग के लिए गति पहुंच के लिए है।

असल में, 'mutable' किसी भी वर्ग विशेषता पर लागू होता है जो ऑब्जेक्ट की बाहरी रूप से दिखाई देने वाली स्थिति को प्रभावित नहीं करता है।

आपके प्रश्न में नमूना कोड में, निष्पादन योग्य अनुचित हो सकता है यदि done_ का मान बाहरी स्थिति को प्रभावित करता है, तो यह इस बात पर निर्भर करता है कि ... में क्या है ...; अंश।


उत्परिवर्तनीय कक्षा के लिए तार्किक आधार पर बिटवाईड कॉन्स से const का अर्थ बदलता है।

इसका मतलब यह है कि उत्परिवर्तनीय सदस्यों के साथ कक्षाएं अब bitwise const हैं और निष्पादन योग्य के केवल-पढ़ने वाले अनुभागों में दिखाई नहीं देगी।

इसके अलावा, यह const_cast का उपयोग किए बिना परिवर्तनीय सदस्यों को बदलने के लिए const सदस्य फ़ंक्शन को अनुमति देकर टाइप-चेकिंग को संशोधित करता है।

class Logical {
    mutable int var;

public:
    Logical(): var(0) {}
    void set(int x) const { var = x; }
};

class Bitwise {
    int var;

public:
    Bitwise(): var(0) {}
    void set(int x) const {
        const_cast<Bitwise*>(this)->var = x;
    }
};

const Logical logical; // Not put in read-only.
const Bitwise bitwise; // Likely put in read-only.

int main(void)
{
    logical.set(5); // Well defined.
    bitwise.set(5); // Undefined.
}

अधिक जानकारी के लिए अन्य उत्तरों देखें लेकिन मैं यह हाइलाइट करना चाहता था कि यह केवल टाइप-सेफ्टी के लिए नहीं है और यह संकलित परिणाम को प्रभावित करता है।


उत्परिवर्तनीय का उपयोग तब किया जाता है जब आपके पास कक्षा के अंदर एक चर होता है जिसे केवल उस वर्ग के भीतर उपयोग किया जाता है ताकि उदाहरण के लिए एक म्यूटेक्स या लॉक हो। यह चर वर्ग के व्यवहार को नहीं बदलता है, लेकिन वर्ग की थ्रेड सुरक्षा को लागू करने के लिए आवश्यक है। इस प्रकार यदि "mutable" के बिना, आप "const" फ़ंक्शंस नहीं कर पाएंगे क्योंकि इस चर को बाहरी दुनिया में उपलब्ध सभी कार्यों में बदलने की आवश्यकता होगी। इसलिए, एक कॉन्स्ट फ़ंक्शन द्वारा सदस्य चर लिखने योग्य बनाने के लिए उत्परिवर्तनीय पेश किया गया था।

म्यूटेबल निर्दिष्ट संकलक और पाठक दोनों को सूचित करता है कि यह सुरक्षित है और उम्मीद है कि एक सदस्य चर को कॉन्स्ट सदस्य फ़ंक्शन के भीतर संशोधित किया जा सकता है।


कुछ मामलों में (खराब डिजाइन किए गए इटरेटर्स की तरह), कक्षा को गिनती या कुछ अन्य आकस्मिक मूल्य रखने की आवश्यकता होती है, जो वास्तव में कक्षा के प्रमुख "राज्य" को प्रभावित नहीं करती है। यह अक्सर होता है जहां मैं उत्परिवर्तनीय उपयोग करता हूं। उत्परिवर्तनीय के बिना, आपको अपने डिजाइन की संपूर्ण कॉन्स्टेस बलिदान देने के लिए मजबूर होना होगा।

यह मुझे लगता है कि ज्यादातर समय एक हैक की तरह लगता है। बहुत कम स्थितियों में उपयोगी।


क्लासिक उदाहरण (जैसा कि अन्य उत्तरों में उल्लिखित है) और एकमात्र स्थिति मैंने अब तक उपयोग किए जाने वाले mutable कीवर्ड को देखा है, जटिल Get विधि के परिणाम को कैश करने के लिए है, जहां कैश को कक्षा के डेटा सदस्य के रूप में लागू किया गया है और नहीं विधि में एक स्थिर चर के रूप में (कई कार्यों या सादे सफाई के बीच साझा करने के कारणों के लिए)।

आम तौर पर, const_cast कीवर्ड का उपयोग करने के विकल्प आमतौर पर विधि या const_cast चाल में एक स्थिर चर होते हैं।

यहां एक और विस्तृत स्पष्टीकरण है।


बहुत ही खोजशब्द 'mutable' वास्तव में एक आरक्षित कीवर्ड है। इसे निरंतर चर के मान को बदलने के लिए उपयोग किया जाता है। अगर आप किसी कॉन्स्टेंट के एकाधिक मान प्राप्त करना चाहते हैं, तो कीवर्ड को म्यूटेबल का उपयोग करें।

//Prototype 
class tag_name{
                :
                :
                mutable var_name;
                :
                :
               };   

म्यूटेबल आसान हो सकता है जब आप एक कॉन्स्ट वर्चुअल फ़ंक्शन ओवरराइड कर रहे हैं और उस फ़ंक्शन में अपने बाल वर्ग सदस्य चर को संशोधित करना चाहते हैं। ज्यादातर मामलों में आप बेस क्लास के इंटरफ़ेस को बदलना नहीं चाहते हैं, इसलिए आपको अपने आप के परिवर्तनीय सदस्य चर का उपयोग करना होगा।


यह bitwise const और तार्किक आधार के भेदभाव की अनुमति देता है। लॉजिकल कॉन्स तब होता है जब कोई ऑब्जेक्ट सार्वजनिक इंटरफ़ेस के माध्यम से दिखाई देने वाले तरीके से नहीं बदलता है, जैसे आपके लॉकिंग उदाहरण। एक और उदाहरण एक वर्ग होगा जो पहली बार अनुरोध किए जाने वाले मूल्य की गणना करता है, और परिणाम कैश करता है।

चूंकि सी ++ 11 mutable का उपयोग लैम्ब्डा पर किया जा सकता है ताकि यह इंगित किया जा सके कि मूल्य द्वारा कैप्चर की गई चीजें संशोधित हैं (वे डिफ़ॉल्ट रूप से नहीं हैं):

int x = 0;
auto f1 = [=]() mutable {x = 42;};  // OK
auto f2 = [=]()         {x = 42;};  // Error: a by-value capture cannot be modified in a non-mutable lambda

यह परिस्थितियों में उपयोगी है जहां आपने कैश जैसे आंतरिक राज्य छुपाया है। उदाहरण के लिए:

class HashTable
{
...
public:
    string lookup(string key) const
    {
        if(key == lastKey)
            return lastValue;

        string value = lookupInternal(key);

        lastKey = key;
        lastValue = value;

        return value;
    }

private:
    mutable string lastKey, lastValue;
};

और फिर आपके पास एक const HashTable ऑब्जेक्ट अभी भी इसकी lookup() विधि का उपयोग कर सकता है, जो आंतरिक कैश को संशोधित करता है।


सबसे अच्छा उदाहरण है जहां हम म्यूटेबल का उपयोग करते हैं, गहरी प्रतिलिपि में है। कॉपी कन्स्ट्रक्टर में हम const &obj को तर्क के रूप में भेजते हैं। तो बनाई गई नई वस्तु निरंतर प्रकार की होगी। अगर हम बदलना चाहते हैं (ज्यादातर हम बदल नहीं पाएंगे, दुर्लभ मामले में हम बदल सकते हैं) इस नव निर्मित कॉन्स ऑब्जेक्ट के सदस्यों को हमें इसे mutable घोषित करने की आवश्यकता है।

mutable स्टोरेज क्लास का उपयोग केवल कक्षा के गैर स्थैतिक गैर-स्थिर डेटा सदस्य पर किया जा सकता है। किसी वर्ग के म्यूटेबल डेटा सदस्य को संशोधित किया जा सकता है भले ही यह किसी ऑब्जेक्ट का हिस्सा हो जिसे कॉन्स के रूप में घोषित किया गया हो।

class Test
{
public:
    Test(): x(1), y(1) {};
    mutable int x;
    int y;
};

int main()
{
    const Test object;
    object.x = 123;
    //object.y = 123;
    /* 
    * The above line if uncommented, will create compilation error.
    */   

    cout<< "X:"<< object.x << ", Y:" << object.y;
    return 0;
}

Output:-
X:123, Y:1

उपर्युक्त उदाहरण में, हम सदस्य चर x के मान को बदलने में सक्षम हैं, हालांकि यह किसी ऑब्जेक्ट का हिस्सा है जिसे कॉन्स्ट के रूप में घोषित किया गया है। ऐसा इसलिए है क्योंकि परिवर्तनीय x को परिवर्तनीय घोषित किया गया है। लेकिन यदि आप सदस्य चर y के मान को संशोधित करने का प्रयास करते हैं, तो संकलक एक त्रुटि फेंक देगा।


mutable कीवर्ड आपके ऑब्जेक्ट्स पर डालने वाले const को छेदने का एक तरीका है। यदि आपके पास किसी ऑब्जेक्ट का कॉन्स्ट रेफरेंस या पॉइंटर है, तो आप उस ऑब्जेक्ट को किसी भी तरीके से संशोधित नहीं कर सकते हैं, सिवाय इसके कि यह कब और कैसे mutable चिह्नित किया जाता है।

आपके const संदर्भ या सूचक के साथ आप को बाध्य किया जाता है:

  • केवल किसी भी दृश्यमान डेटा सदस्यों के लिए पहुंच पढ़ें
  • केवल उन विधियों को कॉल करने की अनुमति है जिन्हें const के रूप में चिह्नित किया गया है।

mutable अपवाद इसे बनाता है ताकि आप अब mutable चिह्नित डेटा सदस्यों को लिख या सेट कर सकें। यह एकमात्र बाहरी रूप से दिखाई देने वाला अंतर है।

आंतरिक रूप से उन const विधियों जो आपके लिए दृश्यमान हैं वे डेटा सदस्यों को भी लिख सकते हैं जो mutable चिह्नित हैं। अनिवार्य रूप से कॉन्स घूंघट व्यापक रूप से छेद किया जाता है। यह एपीआई डिजाइनर तक पूरी तरह से यह सुनिश्चित करने के लिए है कि mutable const अवधारणा को नष्ट नहीं करता है और केवल उपयोगी विशेष मामलों में उपयोग किया जाता है। mutable कीवर्ड मदद करता है क्योंकि यह उन विशेष सदस्यों को स्पष्ट रूप से चिह्नित करता है जो इन विशेष मामलों के अधीन हैं।

अभ्यास में आप अपने कोडबेस में const कॉन्स का उपयोग कर सकते हैं (आप अनिवार्य रूप से अपने कोडबेस को "बीमारी" से संक्रमित करना चाहते हैं)। इस दुनिया के पॉइंटर्स और संदर्भों में बहुत कम अपवाद हैं, जो कोड प्रदान करते हैं जो समझने और समझने में आसान है। एक दिलचस्प digression के लिए "रेफरेंसियल पारदर्शिता" देखो।

mutable कीवर्ड के बिना आपको अंततः विभिन्न उपयोगी विशेष मामलों को संभालने के लिए const_cast का उपयोग करने के लिए मजबूर होना होगा (कैशिंग, रेफ गिनती, डेटा डीबग इत्यादि)। दुर्भाग्यवश const_cast से काफी अधिक विनाशकारी है क्योंकि यह एपीआई क्लाइंट को ऑब्जेक्ट्स (एस) का उपयोग करने वाले ऑब्जेक्ट्स की const सुरक्षा को नष्ट करने के लिए मजबूर करता है। इसके अतिरिक्त यह व्यापक रूप से const_cast विनाश का कारण बनता है: const_cast एक const_cast पॉइंटर या संदर्भ में const_cast लेखन और दृश्य सदस्यों को कॉलिंग विधि की अनुमति देता है। इसके विपरीत mutable को एपीआई डिजाइनर को const अपवादों पर ठीक दाग नियंत्रण का उपयोग करने की आवश्यकता होती है, और आमतौर पर ये अपवाद निजी डेटा पर चलने वाले const विधियों में छिपाए जाते हैं।

(एनबी मैं डेटा और विधि दृश्यता को कुछ बार संदर्भित करता हूं। मैं सार्वजनिक बनाम निजी या संरक्षित के रूप में चिह्नित सदस्यों के बारे में बात कर रहा हूं जो here चर्चा की गई एक पूरी तरह से अलग वस्तु सुरक्षा here ।)





mutable