2011-10-28 21 views
7

हाल ही में, मैं कैसे एकाधिक ही टिककर खेल के साथ डिजाइन करना मुश्किल होता बिना केवल पढ़ने के लिए निजी सदस्यों एक्सेस करने के लिए पर एक दिलचस्प चर्चा पाया, और सुझावों में से एक यह इस तरह से करने के लिए किया गया था:कॉन्स संदर्भ सार्वजनिक सदस्य - यह क्यों काम करता है?

#include <iostream> 

class A { 
public: 
    A() : _ro_val(_val) {} 
    void doSomething(int some_val) { 
    _val = 10*some_val; 
    } 
    const int& _ro_val; 
private: 
    int _val; 
}; 

int main() { 
    A a_instance; 
    std::cout << a_instance._ro_val << std::endl; 
    a_instance.doSomething(13); 
    std::cout << a_instance._ro_val << std::endl; 
} 

आउटपुट:

$ ./a.out 
0 
130 

GotW#66 स्पष्ट रूप से कहा गया है कि वस्तु के जीवनकाल

शुरू होता है जब अपने निर्माता पूरा करता है सफलतापूर्वक और रिटर्न और न ही मैली। यही है, नियंत्रण कन्स्ट्रक्टर बॉडी या पहले के रिटर्न स्टेटमेंट के अंत तक पहुंचता है।

यदि हां, तो हम कोई गारंटी नहीं कि _val सदस्य किया गया है ठीक से बार हम पर अमल _ro_val(_val) द्वारा बनाई होगा। तो उपर्युक्त कोड कैसे काम करता है? क्या यह अनिर्धारित व्यवहार है? या आदिम प्रकारों को वस्तु के जीवनकाल में कुछ अपवाद दिया गया है?

क्या कोई मुझे कुछ संदर्भ में इंगित कर सकता है जो उन चीजों को समझाएगा?

+3

आप इस बारे में चिंतित हैं, तो आप अपनी कक्षा परिभाषा में पहले '_val' घोषित करना चाहिए। सदस्यों को उस क्रम में प्रारंभ किया जाता है जिसमें उन्हें घोषित किया जाता है। (लेकिन जैसा कि उत्तर कहते हैं, आपको इसके संदर्भ में प्रारंभ करने के लिए ऑब्जेक्ट की आवश्यकता नहीं है।) –

+2

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

+1

यदि आप वास्तव में पाते हैं कि आपके गेटर्स और सेटर्स "डिज़ाइन को खराब कर रहे हैं", तो हो सकता है कि आपके डिज़ाइन में कुछ गड़बड़ हो। आप भगवान ऑब्जेक्ट एंटी-पैटर्न का उपयोग कर रहे हैं। Http://stackoverflow.com/questions/565095/java-are-getters-and-setters-evil/565158#565158 – Raedwald

उत्तर

4

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

_ro_val(_val) 

संदर्भ सदस्य _ro_val स्मृति _val के लिए आवंटित है, जो वास्तव में समय के इस बिंदु पर कुछ भी शामिल हो सकता है का उल्लेख करता है।

अभी भी है निर्माता शरीर में अपने कार्यक्रम में एक अपरिभाषित व्यवहार क्योंकि, आप स्पष्ट रूप से _val0 को प्रारंभ करना चाहिए (या कुछ मूल्य, आप चुनते हैं)/इस मामले में सदस्य प्रारंभकर्ता list.The उत्पादन 0 सिर्फ इसलिए कि आप कर रहे हैं भाग्यशाली यह आपको कुछ अन्य मूल्य दे सकता है क्योंकि _val अनइंस्टॉलिज्ड छोड़ दिया गया है। जीसीसी 4.3.4 पर व्यवहार here देखें जो यूबी प्रदर्शित करता है।

लेकिन प्रश्न के लिए, हाँ वास्तव में व्यवहार अच्छी तरह से परिभाषित है।

+1

तो, यदि मैं सही ढंग से समझता हूं, तो ऑब्जेक्ट को संग्रहीत करने के लिए संपूर्ण मेमोरी लेआउट पहले निर्धारित किया गया है हम कन्स्ट्रक्टर तक पहुंचते हैं और इससे '_ro_val_' संदर्भ मान्य होता है, है ना? –

+0

@dare: यह इसका सारांश है, हां। – Xeo

+0

@ dare2be: हाँ, यह सही है। –

1

ऑब्जेक्ट का पता नहीं बदलता है।

आईई। यह अच्छी तरह से परिभाषित है।

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

चीयर्स & hth।,

+0

"_premature optimization_" मैं बजाय समयपूर्व निराशा कहूंगा। – curiousguy

0

मेरी राय में, यह एक गैर-आरंभिकृत वस्तु के साथ एक संदर्भ प्रारंभ करने में कानूनी (अच्छी तरह से परिभाषित) है। यह कानूनी लेकिन मानक है (ठीक है, नवीनतम सी ++ 11 ड्राफ्ट, अनुच्छेद 8.5.3।3) सिफारिश की गई है एक प्रारंभकर्ता के रूप में एक वैध (पूरी तरह से निर्माण) ऑब्जेक्ट का उपयोग:

[Note: in particular, a null reference cannot exist in a well-defined program, because the only way to create such a reference would be to bind it to the “object” obtained by dereferencing a null pointer, which causes undefined behavior.]

:

A reference shall be initialized to refer to a valid object or function.

एक ही पैराग्राफ से अगले वाक्य संदर्भ निर्माण पर थोड़ा और प्रकाश डालता है

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

इसके संदर्भ के माध्यम से अनियमित वस्तु तक पहुंच खतरनाक हो सकती है।

मैं एक साधारण परीक्षण अनुप्रयोग है कि इसके माध्यम से अप्रारंभीकृत वस्तु और उस वस्तु तक पहुँचने के परिणामों से संदर्भ प्रारंभ दर्शाता लिखा है:

class C 
{ 
public: 
    int _n; 

    C() : _n(123) 
    { 
     std::cout << "C::C(): _n = " << _n << " ...and blowing up now!" << std::endl;  
     throw 1; 
    } 
}; 

class B 
{ 
public: 

    // pC1- address of the reference is the address of the object it refers 
    // pC2- address of the object 
    B(const C* pC1, const C* pC2) 
    { 
     std::cout << "B::B(): &_ro_c = " << pC1 << "\n\t&_c = " << pC2 << "\n\t&_ro_c->_n = " << pC1->_n << "\n\t&_c->_n = " << pC2->_n << std::endl; 
    } 
}; 

class A 
{ 
    const C& _ro_c;  
    B _b; 
    C _c; 

public:  

    // Initializer list: members are initialized in the order how they are 
    // declared in class 
    // 
    // Initializes reference to _c 
    // 
    // Fully constructs object _b; its c-tor accesses uninitialized object 
    // _c through its reference and its pointer (valid but dangerous!) 
    // 
    // construction of _c fails! 
    A() : _ro_c(_c), _b(&_ro_c, &_c), _c() 
    { 
     // never executed 
     std::cout << "A::A()" << std::endl; 
    } 
}; 

int main() 
{ 
    try 
    { 
     A a; 
    } 
    catch(...) 
    { 
     std::cout << "Failed to create object of type A" << std::endl; 
    } 

    return 0; 
} 

आउटपुट:

B::B(): &_ro_c = 001EFD70 
     &_c = 001EFD70 
     &_ro_c->_n = -858993460 
     &_c->_n = -858993460 
C::C(): _n = 123 ...and blowing up now! 
Failed to create object of type A 
संबंधित मुद्दे