c++ - कॉन्स int*, const int*const, और int const*के बीच क्या अंतर है?




pointers (9)

मैं हमेशा गड़बड़ करता हूं कि कैसे const int* , const int * const , और int const * सही तरीके से उपयोग करें। क्या नियमों का एक सेट परिभाषित करता है कि आप क्या कर सकते हैं और क्या नहीं कर सकते?

मैं असाइनमेंट के मामले में सभी कार्यों और सभी डॉन को जानना चाहता हूं, कार्यों को पारित करना आदि।


  1. निरंतर संदर्भ:

    एक चर (यहां int) का संदर्भ, जो निरंतर है। हम वैरिएबल को मुख्य रूप से संदर्भ के रूप में पास करते हैं, क्योंकि संदर्भ वास्तविक मान से आकार में छोटे होते हैं, लेकिन एक दुष्प्रभाव होता है और ऐसा इसलिए होता है क्योंकि यह वास्तविक चर के उपनाम की तरह है। हम उपरोक्त रूप से हमारी पूर्ण पहुंच के माध्यम से मुख्य चर को बदल सकते हैं, इसलिए हम इसे इस दुष्प्रभाव को रोकने के लिए निरंतर बनाते हैं।

    int var0 = 0;
    const int &ptr1 = var0;
    ptr1 = 8; // Error
    var0 = 6; // OK
    
  2. लगातार पॉइंटर्स

    एक बार एक स्थिर सूचक एक चर के लिए इंगित करता है तो यह किसी अन्य चर को इंगित नहीं कर सकता है।

    int var1 = 1;
    int var2 = 0;
    
    int *const ptr2 = &var1;
    ptr2 = &var2; // Error
    
  3. स्थिर करने के लिए सूचक

    एक पॉइंटर जिसके माध्यम से कोई एक चर के मान को नहीं बदल सकता है, इसे पॉइंटर के रूप में जाना जाता है।

    int const * ptr3 = &var2;
    *ptr3 = 4; // Error
    
  4. निरंतर सूचकांक निरंतर

    निरंतर एक स्थिर सूचक एक सूचक है जो न तो उस पते को बदल सकता है जो यह इंगित कर रहा है और न ही उस पते पर रखे गए मूल्य को बदल सकता है।

    int var3 = 0;
    int var4 = 0;
    const int * const ptr4 = &var3;
    *ptr4 = 1;     // Error
     ptr4 = &var4; // Error
    

'कॉन्स' का सरल उपयोग

सबसे सरल उपयोग नामित निरंतर घोषित करना है। ऐसा करने के लिए, एक निरंतर घोषित करता है जैसे कि यह एक चर था लेकिन इससे पहले 'कॉन्स्ट' जोड़ें। इसे तुरंत निर्माता में शुरू करना होगा क्योंकि, निश्चित रूप से, कोई मूल्य बाद में सेट नहीं कर सकता है क्योंकि यह इसे बदल देगा। उदाहरण के लिए,

const int Constant1=96; 

मूल्य 96 के साथ एक पूर्णांक स्थिर, अकल्पनीय रूप से 'कॉन्स्टेंट 1' कहा जाएगा।

ऐसे स्थिरांक प्रोग्राम में उपयोग किए जाने वाले पैरामीटर के लिए उपयोगी होते हैं लेकिन प्रोग्राम संकलित होने के बाद उन्हें बदलने की आवश्यकता नहीं होती है। यह सी प्रीप्रोसेसर '#define' कमांड पर प्रोग्रामर के लिए एक लाभ है जिसमें यह संकलक स्वयं द्वारा समझा जाता है और इसका उपयोग मुख्य संकलक तक पहुंचने से पहले प्रीप्रोसेसर द्वारा प्रोग्राम टेक्स्ट में प्रतिस्थापित नहीं किया जाता है, इसलिए त्रुटि संदेश अधिक सहायक होते हैं ।

यह पॉइंटर्स के साथ भी काम करता है लेकिन किसी को यह निर्धारित करने के लिए 'कॉन्स्ट' होना चाहिए कि पॉइंटर या जो भी इंगित करता है वह निरंतर या दोनों है। उदाहरण के लिए,

const int * Constant2 

घोषणा करता है कि कॉन्स्टेंट 2 एक स्थिर पूर्णांक के लिए परिवर्तनीय सूचक है

int const * Constant2

एक वैकल्पिक वाक्यविन्यास है जो वही करता है, जबकि

int * const Constant3

घोषणा करता है कि कॉन्स्टेंट 3 एक परिवर्तनीय पूर्णांक के लिए निरंतर सूचक है

int const * const Constant4

घोषणा करता है कि कॉन्स्टेंट 4 निरंतर पूर्णांक के लिए निरंतर सूचक है। असल में 'कॉन्स' जो भी तत्काल बायीं ओर है, उसके अलावा लागू होता है (अगर वहां कुछ भी नहीं है, तो यह किस मामले में लागू होता है)।

रेफरी: http://duramecho.com/ComputerInformation/WhyHowCppConst.html


उन लोगों के लिए जो क्लॉकवाइज / सर्पिल नियम के बारे में नहीं जानते हैं: वेरिएबल के नाम से शुरू करें, घड़ी के अनुसार (इस मामले में, पिछड़े स्थानांतरित करें) को अगले पॉइंटर या टाइप पर ले जाएं । अभिव्यक्ति समाप्त होने तक दोहराएं।

यहां एक डेमो है:


जब तक मैं सी ++ गुरु स्कॉट मेयर्स द्वारा इस book आया तब तक मुझे वही संदेह था। इस पुस्तक में तीसरा आइटम देखें जहां वह const का उपयोग करने के बारे में विवरण में बात करता है।

बस इस सलाह का पालन करें

  1. यदि const तारांकन के बाईं ओर दिखाई देता है, तो क्या इंगित किया जाता है निरंतर है
  2. यदि const तारांकन के दाईं ओर दिखाई देता है, तो पॉइंटर स्वयं स्थिर होता है
  3. यदि दोनों तरफ const दिखाई देता है, तो दोनों स्थिर होते हैं

मुझे लगता है कि सबकुछ पहले ही यहां दिया गया है, लेकिन मैं सिर्फ यह जोड़ना चाहता हूं कि आपको typedef सावधान रहना चाहिए! वे सिर्फ टेक्स्ट प्रतिस्थापन नहीं हैं।

उदाहरण के लिए:

typedef char *ASTRING;
const ASTRING astring;

astring का प्रकार char * const , न कि const char * । यह एक कारण है कि मैं हमेशा इस प्रकार के दाहिने हिस्से को const हूं, और शुरुआत में कभी नहीं।


मूल डिजाइनरों द्वारा सी और सी ++ घोषणा वाक्यविन्यास को बार-बार एक असफल प्रयोग के रूप में वर्णित किया गया है।

इसके बजाए, चलिए "पॉइंटर टू Type " Type ; मैं इसे Ptr_ :

template< class Type >
using Ptr_ = Type*;

अब Ptr_<char> लिए एक सूचक है।

Ptr_<const char> को पॉइंटर है।

और const Ptr_<const char> लिए एक const Ptr_<const char> पॉइंटर है।

क्या आप वहां मौजूद हैं.


यह ज्यादातर दूसरी पंक्ति को संबोधित करता है: सर्वोत्तम प्रथाओं, असाइनमेंट्स, फ़ंक्शन पैरामीटर इत्यादि।

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

Const_cast <> प्लेग की तरह से बचें। इसके लिए एक या दो वैध उपयोग मामले हैं, लेकिन वे बहुत कम और बहुत दूर हैं। यदि आप एक const ऑब्जेक्ट को बदलने की कोशिश कर रहे हैं, तो आप यह भी ढूंढने के लिए बहुत बेहतर करेंगे कि जिसने इसे पहली गति में घोषित किया है और इस बात पर सहमति व्यक्त करने के लिए इस मामले पर बात करें कि क्या होना चाहिए।

जो असाइनमेंट में बहुत अच्छी तरह से नेतृत्व करता है। आप केवल तभी कुछ असाइन कर सकते हैं जब यह गैर-आधार है। यदि आप किसी चीज में असाइन करना चाहते हैं, तो ऊपर देखें। याद रखें कि घोषणाओं में int const *foo; और int * const bar; अलग-अलग चीजें हैं - यहां अन्य उत्तरों ने उस मुद्दे को सराहनीय रूप से कवर किया है, इसलिए मैं इसमें नहीं जाऊंगा।

फंक्शन पैरामीटर:

मूल्य से void func(int param) : उदाहरण के लिए void func(int param) आप कॉलिंग साइट पर एक तरफ या दूसरे परवाह नहीं करते हैं। तर्क दिया जा सकता है कि फ़ंक्शन को void func(int const param) रूप में घोषित करने के लिए उपयोग के मामले हैं, लेकिन कॉलर पर इसका कोई प्रभाव नहीं पड़ता है, केवल फ़ंक्शन पर ही, उसमें जो भी मूल्य पारित किया जाता है, उसके दौरान फ़ंक्शन द्वारा बदला नहीं जा सकता कॉल।

संदर्भ द्वारा पास करें: उदाहरण के लिए void func(int &param) अब यह एक फर्क पड़ता है। जैसा कि घोषित किया गया है कि func को param को बदलने की अनुमति है, और किसी भी कॉलिंग साइट को परिणामों से निपटने के लिए तैयार होना चाहिए। void func(int const &param) में घोषणा को बदलना अनुबंध को बदलता है, और गारंटी देता है कि func अब param बदल नहीं सकता है, जिसका अर्थ है कि क्या पारित किया गया है, वह वापस आ जाएगा। जैसा कि अन्य ने ध्यान दिया है कि यह एक बड़ी वस्तु को सस्ती रूप से पारित करने के लिए बहुत उपयोगी है जिसे आप बदलना नहीं चाहते हैं। एक संदर्भ पारित करना एक बड़ी वस्तु को मूल्य से गुजरने से बहुत सस्ता है।

पॉइंटर द्वारा पास करें: उदाहरण के लिए void func(int *param) और void func(int const *param) ये दोनों उनके संदर्भ समकक्षों के साथ बहुत समानार्थी हैं, चेतावनी के साथ कि जिसे फ़ंक्शन को अब nullptr जांच करने की आवश्यकता है जब तक कि कुछ अन्य संविदात्मक गारंटी न हो func आश्वासन देता है कि यह कभी भी param में एक nullptr प्राप्त नहीं होगा।

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

दिन के अंत में सभी उपरोक्त पॉइंटर्स के संदर्भों को हमेशा पसंद करते हैं। वे सिर्फ पूरे दौर में सुरक्षित हैं।


यह सवाल ठीक से दिखाता है कि मैं अपने प्रश्न में जिस तरह से उल्लेख करता हूं, वैसे ही मैं टाइप करना चाहता हूं, टाइप आईडी स्वीकार्य है?

संक्षेप में, मुझे नियम याद रखने का सबसे आसान तरीका यह है कि "कॉन्स" उस चीज़ के बाद चला जाता है जिस पर यह लागू होता है। तो आपके प्रश्न में, "int const *" का अर्थ है कि int स्थिर है, जबकि "int * const" का अर्थ यह होगा कि सूचक स्थिर है।

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

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


सी ++ में कॉन्स शुद्धता के आस-पास कई अन्य सूक्ष्म बिंदु हैं। मुझे लगता है कि यहां सवाल सी के बारे में बस रहा है, लेकिन मैं कुछ संबंधित उदाहरण दूंगा क्योंकि टैग सी ++ है:

  • आप अक्सर बड़े तर्क जैसे तारों के रूप में तारों को पार करते हैं TYPE const & जो ऑब्जेक्ट को या तो संशोधित या कॉपी करने से रोकता है। उदाहरण :

    TYPE& TYPE::operator=(const TYPE &rhs) { ... return *this; }

    लेकिन TYPE & const व्यर्थ है क्योंकि संदर्भ हमेशा स्थिर होते हैं।

  • आपको कक्षा वर्गों को हमेशा लेबल करना चाहिए जो क्लास को const रूप में संशोधित नहीं करते हैं, अन्यथा आप विधि को किसी TYPE const & संदर्भ TYPE const & संदर्भ से कॉल नहीं कर सकते हैं। उदाहरण :

    bool TYPE::operator==(const TYPE &rhs) const { ... }

  • ऐसी सामान्य स्थितियां हैं जहां वापसी मूल्य और विधि दोनों का होना चाहिए। उदाहरण :

    const TYPE TYPE::operator+(const TYPE &rhs) const { ... }

    वास्तव में, कॉन्स विधियों को संदर्भ-से-गैर-कॉन्स्ट के रूप में आंतरिक कक्षा डेटा वापस नहीं करना चाहिए।

  • नतीजतन, किसी को अक्सर कॉन्स ओवरलोडिंग का उपयोग करके एक कॉन्स और गैर-कॉन्स विधि दोनों बनाना चाहिए। उदाहरण के लिए, यदि आप T const& operator[] (unsigned i) const; को परिभाषित करते हैं T const& operator[] (unsigned i) const; , तो आप शायद गैर-कॉन्स संस्करण भी चाहते हैं:

    inline T& operator[] (unsigned i) { return const_cast<char&>( static_cast<const TYPE&>(*this)[](i) ); }

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





const