C ++ में "const" के उपयोग कितने और कौन से हैं?


129

नौसिखिया सी ++ प्रोग्रामर के रूप में कुछ निर्माण हैं जो अभी भी मुझे बहुत अस्पष्ट लगते हैं, इनमें से एक है const। आप इसे कई स्थानों पर और इतने सारे अलग-अलग प्रभावों के साथ उपयोग कर सकते हैं जो एक शुरुआती के लिए जीवित बाहर आना लगभग असंभव है। क्या कुछ सी ++ गुरु एक बार हमेशा के लिए विभिन्न उपयोगों की व्याख्या करेंगे और क्या और / या क्यों नहीं उनका उपयोग करें?


ठीक उसी प्रश्न की तलाश में: D
alamin

जवाबों:


100

कुछ उपयोगों को इकट्ठा करने की कोशिश कर रहा है:

अपने जीवनकाल को लंबा करने के लिए, संदर्भ-से-कॉन्स्टेंट के लिए कुछ अस्थायी बांधना। संदर्भ एक आधार हो सकता है - और इसके विध्वंसक को आभासी होने की आवश्यकता नहीं है - सही विध्वंसक को अभी भी कहा जाता है:

ScopeGuard const& guard = MakeGuard(&cleanUpFunction);

स्पष्टीकरण , कोड का उपयोग कर:

struct ScopeGuard { 
    ~ScopeGuard() { } // not virtual
};

template<typename T> struct Derived : ScopeGuard { 
    T t; 
    Derived(T t):t(t) { }
    ~Derived() {
        t(); // call function
    }
};

template<typename T> Derived<T> MakeGuard(T t) { return Derived<T>(t); }

इस ट्रिक का उपयोग अलेक्जेंड्रेस्कु के स्कोपगार्ड उपयोगिता वर्ग में किया जाता है। एक बार अस्थायी दायरे से बाहर हो जाने पर, व्युत्पन्न के विध्वंसक को सही ढंग से कहा जाता है। उपरोक्त कोड कुछ छोटे विवरणों को याद करता है, लेकिन इसके साथ बड़ी बात है।


दूसरों को यह बताने के लिए कि इस विधि की तार्किक स्थिति नहीं बदलेगी, का उपयोग करें।

struct SmartPtr {
    int getCopies() const { return mCopiesMade; }
};

कॉपी-ऑन-राइट कक्षाओं के लिए कास्ट का उपयोग करें , कंपाइलर को यह तय करने में मदद करें कि आपको कब और कब कॉपी करने की आवश्यकता नहीं है।

struct MyString {
    char * getData() { /* copy: caller might write */ return mData; }
    char const* getData() const { return mData; }
};

स्पष्टीकरण : आप डेटा साझा करना चाहते हैं जब आप कुछ कॉपी कर सकते हैं जब तक कि मूल रूप से और कोपीईड ऑब्जेक्ट का डेटा समान रहेगा। एक बार जब कोई ऑब्जेक्ट डेटा बदलता है, तो आपको अभी दो संस्करणों की आवश्यकता होती है: एक मूल के लिए, और एक कॉपी के लिए। यही है, आप किसी भी ऑब्जेक्ट पर लिखने पर कॉपी करते हैं, ताकि वे अब दोनों का अपना संस्करण हो।

कोड का उपयोग करना :

int main() {
    string const a = "1234";
    string const b = a;
    // outputs the same address for COW strings
    cout << (void*)&a[0] << ", " << (void*)&b[0];
}

उपरोक्त स्निपेट मेरे GCC पर एक ही पता छापता है, क्योंकि प्रयुक्त C ++ लाइब्रेरी कॉपी-ऑन-राइट को लागू करता है std::string। दोनों स्ट्रिंग्स, भले ही वे अलग-अलग ऑब्जेक्ट हों, अपने स्ट्रिंग डेटा के लिए समान मेमोरी साझा करते हैं। bनॉन-कास्ट बनाना गैर-कास्ट संस्करण को पसंद करेगा operator[]और जीसीसी बैकिंग मेमोरी बफर की एक प्रति बनाएगा, क्योंकि हम इसे बदल सकते हैं और इसे डेटा को प्रभावित नहीं करना चाहिए a!

int main() {
    string const a = "1234";
    string b = a;
    // outputs different addresses!
    cout << (void*)&a[0] << ", " << (void*)&b[0];
}

कॉपी-कंस्ट्रक्टर के लिए कांस्टेबल ऑब्जेक्ट्स और टेम्परेरी से कॉपी बनाना :

struct MyClass {
    MyClass(MyClass const& that) { /* make copy of that */ }
};

स्थिरांक बनाने के लिए जो तुच्छ रूप से बदल नहीं सकते हैं

double const PI = 3.1415;

मूल्य के बजाय संदर्भ द्वारा मनमानी वस्तुओं को पारित करने के लिए - संभवतः महंगा या असंभव द्वारा पारित मूल्य को रोकने के लिए

void PrintIt(Object const& obj) {
    // ...
}

2
क्या आप अपने उदाहरणों में पहला और तीसरा उपयोग बता सकते हैं?
त्ननुज

"कैली की गारंटी के लिए कि पैरामीटर NULL नहीं हो सकता है" मैं नहीं देखता कि कैसे कास्ट का उस उदाहरण के साथ कुछ भी नहीं है।
लोगान कैपल्डो

उफ़, मैं बहुत असफल रहा। मैंने किसी तरह संदर्भों के बारे में लिखना शुरू किया। कराह रही है के लिए बहुत बहुत धन्यवाद :) मैं निश्चित रूप से अब है कि सामान को निकाल देंगे :)
Johannes Schaub - litb

3
कृपया पहला उदाहरण स्पष्ट करें। मेरे लिए बहुत मायने नहीं रखता है।
चिकुबा

28

C ++ में वास्तव में const के 2 मुख्य उपयोग हैं।

कांस्टिट्यूशन मान

यदि एक मान एक चर, सदस्य, या पैरामीटर के रूप में है, जो अपने जीवनकाल के दौरान बदल नहीं जाएगा (या नहीं होना चाहिए) तो आपको इसे चिह्नित करना चाहिए। यह ऑब्जेक्ट पर म्यूटेशन को रोकने में मदद करता है। उदाहरण के लिए, निम्न फ़ंक्शन में मुझे विद्यार्थी की आवृत्ति को बदलने की आवश्यकता नहीं है, इसलिए मैंने इसे कास्ट मार्क किया।

void PrintStudent(const Student& student) {
  cout << student.GetName();
}

जैसा कि आप ऐसा क्यों करेंगे। एल्गोरिथ्म के बारे में तर्क करना बहुत आसान है यदि आप जानते हैं कि अंतर्निहित डेटा नहीं बदल सकता है। "const" मदद करता है, लेकिन इसकी गारंटी नहीं है कि यह हासिल किया जाएगा।

जाहिर है, डेटा को कोउट में प्रिंट करने के लिए ज्यादा विचार की आवश्यकता नहीं है :)

एक सदस्य विधि को कॉन्स्टेबल के रूप में चिह्नित करना

पिछले उदाहरण में मैंने स्टूडेंट को कास्ट के रूप में चिह्नित किया। लेकिन C ++ को कैसे पता चला कि स्टूडेंट पर गेटनेम () मेथड को कॉल करने से ऑब्जेक्ट म्यूट नहीं होगा? इसका उत्तर यह है कि विधि कांस्टेबल के रूप में चिह्नित की गई थी।

class Student {
  public:
    string GetName() const { ... }
};

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

यह एक बहुत ही सरल उदाहरण है, लेकिन उम्मीद है कि यह आपके सवालों के जवाब देने में मदद करेगा।


16

इन 4 घोषणाओं के बीच के अंतर को समझने के लिए ध्यान रखें:

निम्नलिखित 2 घोषणाएँ शब्दार्थ समान हैं। आप जहाँ ccp1 और ccp2 बिंदु बदल सकते हैं , लेकिन आप उस चीज़ को नहीं बदल सकते हैं जिस पर वे इशारा कर रहे हैं।

const char* ccp1;
char const* ccp2;

इसके बाद, पॉइंटर कांस्टेबल है, इसलिए सार्थक होने के लिए इसे किसी चीज़ की ओर इशारा करना चाहिए। आप इसे किसी और चीज़ की ओर इशारा नहीं कर सकते, हालाँकि यह जिस चीज़ को इंगित करता है उसे बदला जा सकता है।

char* const cpc = &something_possibly_not_const;

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

const char* const ccpc = &const_obj;

दक्षिणावर्त सर्पिल नियम एक घोषणा को सुलझाने में मदद कर सकता है http://c-faq.com/decl/spiral.anderson.html


एक चक्कर में, हाँ, यह करता है! दक्षिणावर्त सर्पिल नियम इसे बेहतर बताते हैं - नाम (kpPointer) पर शुरू करें और टोकन के माध्यम से बाहर जाने वाले दक्षिणावर्त सर्पिल को आकर्षित करें, और प्रत्येक टोकन को कहें। जाहिर है, kpPointer के अधिकार के लिए कुछ भी नहीं है, लेकिन यह अभी भी काम करता है।
स्टीव फोली

3

एक छोटे नोट के रूप में, जैसा कि मैंने यहां पढ़ा , यह नोटिस करना उपयोगी है

const जो कुछ भी इसके तत्काल बाईं ओर है (इसके अलावा अगर वहाँ कुछ भी नहीं है तो यह उस पर लागू होता है जो इसके तत्काल अधिकार पर लागू होता है)।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.