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




keyword (16)

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

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

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


Answers

इसका उपयोग एक हैक नहीं है, हालांकि सी ++ में कई चीजों की तरह, म्यूटेबल एक आलसी प्रोग्रामर के लिए हैक हो सकता है जो सभी तरह से वापस नहीं जाना चाहता और कुछ ऐसा चिह्नित नहीं करता जो गैर-कॉन्स के रूप में नहीं होना चाहिए।


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


उत्परिवर्तनीय कक्षा के लिए तार्किक आधार पर बिटवाईड कॉन्स से 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.
}

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


सबसे अच्छा उदाहरण है जहां हम म्यूटेबल का उपयोग करते हैं, गहरी प्रतिलिपि में है। कॉपी कन्स्ट्रक्टर में हम 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 के मान को संशोधित करने का प्रयास करते हैं, तो संकलक एक त्रुटि फेंक देगा।


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

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


खैर, हाँ, यही वह करता है। मैं उन सदस्यों के लिए इसका उपयोग करता हूं जो विधियों द्वारा संशोधित हैं जो तर्कसंगत रूप से किसी वर्ग की स्थिति को परिवर्तित नहीं करते हैं - उदाहरण के लिए, कैश को लागू करके लुकअप को तेज़ करने के लिए:

class CIniWrapper
{
public:
   CIniWrapper(LPCTSTR szIniFile);

   // non-const: logically modifies the state of the object
   void SetValue(LPCTSTR szName, LPCTSTR szValue);

   // const: does not logically change the object
   LPCTSTR GetValue(LPCTSTR szName, LPCTSTR szDefaultValue) const;

   // ...

private:
   // cache, avoids going to disk when a named value is retrieved multiple times
   // does not logically change the public interface, so declared mutable
   // so that it can be used by the const GetValue() method
   mutable std::map<string, string> m_mapNameToValue;
};

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


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

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

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


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

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

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


mutable मौजूद है क्योंकि आप किसी अन्यथा निरंतर फ़ंक्शन में डेटा को संशोधित करने की अनुमति देते हैं।

इरादा यह है कि आपके पास एक ऐसा फ़ंक्शन हो सकता है जो ऑब्जेक्ट की आंतरिक स्थिति में "कुछ भी नहीं" हो, और इसलिए आप फ़ंक्शन const चिह्नित करते हैं, लेकिन आपको वास्तव में कुछ ऑब्जेक्ट्स राज्य को संशोधित करने की आवश्यकता हो सकती है जो इसके प्रभाव को प्रभावित नहीं करती सही कार्यक्षमता।

कीवर्ड कंपाइलर के संकेत के रूप में कार्य कर सकता है - एक सैद्धांतिक संकलक स्मृति में निरंतर वस्तु (जैसे वैश्विक) रख सकता है जिसे केवल पढ़ने के लिए चिह्नित किया गया था। mutable संकेतों की उपस्थिति कि यह नहीं किया जाना चाहिए।

परिवर्तनीय डेटा घोषित करने और उपयोग करने के कुछ वैध कारण यहां दिए गए हैं:

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

यह 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

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


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


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

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

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

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() विधि का उपयोग कर सकता है, जो आंतरिक कैश को संशोधित करता है।


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 ।)


नहीं, file एक बिलिन है, न कि कोई कीवर्ड:

>>> import keyword
>>> keyword.iskeyword('file')
False
>>> import __builtin__
>>> hasattr(__builtin__, 'file')
True

इसे open() लिए एक उपनाम के रूप में देखा जा सकता है open() , लेकिन इसे पायथन 3 से हटा दिया गया है, क्योंकि नए io ढांचे ने इसे बदल दिया है। तकनीकी रूप से, यह open() फ़ंक्शन द्वारा लौटाए गए ऑब्जेक्ट का प्रकार है







c++ keyword mutable