2010-04-08 17 views
14

मैं कोड है कि इस तरह दिखता है। हालांकि, यह अन्यथा प्रकट होता है, कंटेनर के निर्माण के बाद एडाप्टर ऑब्जेक्ट नष्ट हो जाता है, जिससे लटकती संदर्भ छोड़ दिया जाता है।सी ++ निरंतर संदर्भ जीवनकाल (कंटेनर अनुकूलक)

सही जीवनकाल क्या है?

एडाप्टर अस्थायी ऑब्जेक्ट का कंटेनर ऑब्जेक्ट या कंटेनर कन्स्ट्रक्टर का दायरा है?

कक्षा सदस्य संदर्भ में बाध्यकारी अस्थायी वस्तु को सही ढंग से कार्यान्वित करने के तरीके को कैसे कार्यान्वित करें?

धन्यवाद

उत्तर

16

सी ++ 03 मानक के अनुसार, किसी संदर्भ के लिए एक अस्थायी बाध्य संदर्भ के आधार पर अलग-अलग जीवनकाल है। अपने उदाहरण में, मुझे लगता है कि नीचे दिए गए मुख्य भाग (12.2/5 "अस्थाई वस्तुओं") लागू होता है:

अस्थायी करने के लिए जो संदर्भ बाध्य है या अस्थायी एक subobject को पूरा वस्तु है कि जो अस्थायी की जैसा कि नीचे निर्दिष्ट है, संदर्भ के जीवनकाल के लिए बाध्य है। एक कन्स्ट्रक्टर के सीटीओ-प्रारंभकर्ता (12.6.2) में एक संदर्भ सदस्य के लिए एक अस्थायी बाध्य जब तक कि निर्माता बाहर निकलता रहता है। फ़ंक्शन कॉल (5.2.2) में संदर्भ पैरामीटर के लिए अस्थायी बाध्य कॉल युक्त पूर्ण अभिव्यक्ति को पूरा होने तक जारी रहता है।

इसलिए जब एक अस्थायी रूप से बाध्यकारी एक उन्नत तकनीक अस्थायी वस्तु (GotW #88: A Candidate For the "Most Important const") के जीवन का विस्तार करने के लिए है, यह जाहिरा तौर पर आप इस मामले में मदद नहीं करेगा।

दूसरी तरफ, एरिक नीबलर का एक ऐसा लेख है जिसमें आपको रुचि हो सकती है जिसमें एक रोचक (अगर गठित) तकनीक पर चर्चा हो सकती है जो आपके वर्ग के रचनाकारों को यह बता सकती है कि क्या एक अस्थायी वस्तु (वास्तव में एक रावल्यू) इसे पास कर दी गई है या नहीं एक गैर-अस्थायी (lvalue) (और इसलिए कॉपी किया जा करने के लिए होता है) या पारित किया गया के रूप में (और इसलिए संभवतः सुरक्षित रूप से एक संदर्भ नकल के बजाय रखे किया जा सकता था):

अच्छा इसके साथ भाग्य हालांकि - हर बार जब मैं लेख पढ़ता हूं, मैं सब कुछ के माध्यम से काम करना है जैसे कि मैंने पहले कभी सामग्री नहीं देखी है। यह केवल एक क्षणिक क्षण के लिए मेरे साथ चिपक जाता है ...

और मुझे यह उल्लेख करना चाहिए कि सी ++ 0x के रावल्यू संदर्भों से निबेलर की तकनीक अनावश्यक होनी चाहिए। रावल्यू संदर्भों को एमएसवीसी 2010 द्वारा समर्थित किया जाएगा जो एक हफ्ते में जारी किया जाना निर्धारित है (अगर 12 अप्रैल 2010 को मुझे सही याद है)। मुझे नहीं पता कि जीसीसी में रावल संदर्भों की स्थिति क्या है।

+0

मुझे लगता है कि वास्तव में इस मामले में अस्थायी अगली वाक्य में एक फ़ंक्शन कॉल पैरामीटर (कन्स्ट्रक्टर कॉल) से जुड़ा हुआ है। हां, यह सीटीओ प्रारंभकर्ता में एलियासिंग की वजह से सदस्य के लिए भी बाध्य है, और हां, यह तब तक जारी रहेगा जब तक कि निर्माता बाहर निकलता है (वास्तव में, अगर कन्स्ट्रक्टर कॉल युक्त पूर्ण अभिव्यक्ति भी अन्य चीजें करता है)। लेकिन मुझे लगता है कि हाइलाइट किया गया मार्ग 'स्ट्रक्चर कंटेनर {कॉन्स एंड एडेप्टर ए' जैसी चीजों को संदर्भित करता है; कंटेनर(): ए (एडाप्टर()) {}}; '। –

+0

@Steve: नज़दीक देखो, मुझे लगता है कि आप सही हैं - मैं जवाब अपडेट करूंगा (हालांकि एक ही परिणाम)। –

6

अस्थाई स्थिरांक संदर्भ केवल वर्तमान बयान के जीवनकाल है (जो है, वे सिर्फ सेमी-कोलन से पहले क्षेत्र से बाहर जाना)। तो अंगूठे का नियम उस कार्य के जीवनकाल से बाहर मौजूद एक कॉन्स्ट-रेफरेंस पर भरोसा नहीं करता है जो इसे पैरामीटर के रूप में प्राप्त करता है, इस मामले में यह केवल निर्माता है। तो एक बार कन्स्ट्रक्टर पूरा हो जाने के बाद, अभी भी आसपास के किसी भी कॉन्स संदर्भ पर भरोसा न करें।

अस्थायी लोगों के लिए इस जीवनकाल को बदलने/ओवरराइड/विस्तार करने का कोई तरीका नहीं है। बेहतर अभी तक

adapter a, b; 
container(a, b); // lifetime is the lifetime of a and b 

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

+2

अधिक सटीक होना, वे पूर्ण अभिव्यक्ति वे में बनाया गया के अंत तक रहते हैं – GManNickG

+1

"कोई रास्ता नहीं बदलने के लिए/ओवरराइड/temporaries के लिए इस जीवन का विस्तार नहीं है।" - वास्तव में नहीं है, यह सिर्फ नहीं है इस तरह के मामलों में उपयोगी। यदि आप स्वचालित अवधि के साथ एक कॉन्स्ट संदर्भ प्रारंभ करने के लिए अस्थायी का उपयोग करते हैं, तो अस्थायी का जीवनकाल तब तक बढ़ाया जाता है जब तक कि स्वचालित का दायरा बाहर नहीं हो जाता है। –

1

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

आप अपने वर्ग को फिर से परिभाषित के रूप में करना चाहिए:

 
template<typename T> 
class container 
{ 
    public: 
     container(const T& first, const T& second) : first(first), second(second) {} 
    private: 
     const T first; 
     const T second; 
}; 

फिर, आप अपने वस्तुओं उन्हें क्षेत्र से बाहर जाने से रोकने के लिए कोई नाम दे सकते हैं:

 
    adaptor first; 
    adaptor second; 
    container c(first,second); 

हालांकि, मुझे नहीं लगता कि यह एक अच्छा विचार है, क्योंकि return c जैसे बयान अवैध है।

संपादित
अपने लक्ष्य को आदेश नकल की लागत से बचने के लिए वस्तुओं साझा करने के लिए है, तो आप स्मार्ट सूचक वस्तुओं के उपयोग पर विचार करना चाहिए।

 
template<typename T> 
class container 
{ 
    public: 
     container(const boost::shared_ptr<const T>& first, const boost::shared_ptr<const T>& second) : first(first), second(second) {} 
    private: 
     boost::shared_ptr<const T> first; 
     boost::shared_ptr<const T> second; 
}; 

फिर आप उपयोग कर सकते हैं:

 
boost::shared_ptr<const adaptor> first(new adaptor); 
boost::shared_ptr<const adaptor> second(new adaptor); 
container<adaptor> c(first,second); 

या, यदि आप स्थानीय स्तर पर पहले और दूसरे की परिवर्तनशील प्रतियां हैं:

उदाहरण के लिए, हम इस प्रकार स्मार्ट संकेत का उपयोग कर अपने वस्तु को फिर से परिभाषित कर सकते हैं
 
boost::shared_ptr<adaptor> first(new adaptor); 
boost::shared_ptr<adaptor> second(new adaptor); 
container<adaptor> c(boost::const_pointer_cast<const adaptor>(first),boost::const_pointer_cast<const adaptor>(second)); 
+0

वास्तविक वस्तुओं साइड इफेक्ट्स कन्स्ट्रक्टर के साथ काफी भारी हैं। मैं कॉपी निर्माण से बचने की कोशिश कर रहा हूं। – Anycorn

+2

@aaa, उस स्थिति में, आपको बूस्ट :: shared_ptr जैसे स्मार्ट पॉइंटर्स का उपयोग करना चाहिए। –

+0

मैंने ऐसा करने के बारे में सोचा, हालांकि कक्षा सार्वजनिक इंटरफ़ेस में है जो बढ़ावा देने को मुक्त रखने की कोशिश कर रहा है – Anycorn

-1

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

यही कारण है कि मैं सदस्यों को संदर्भ के रूप में कभी भी स्टोर नहीं करता - केवल कॉपी ऑब्जेक्ट्स या पॉइंटर्स। मेरे लिए, पॉइंटर्स यह स्पष्ट करते हैं कि जीवन भर खेलने के लिए आता है। खासकर एक कन्स्ट्रक्टर के मामले में, यह स्पष्ट नहीं है कि आपके कन्स्ट्रक्टर पैराम्स को कक्षा को ही बाहर निकालना होगा।

+1

-1: पॉइंटर्स को जब भी संभव हो संदर्भों के साथ प्रतिस्थापित किया जाना चाहिए। –

+0

मैंने -1 नहीं किया, लेकिन वे पूरी अभिव्यक्ति के अंत तक जीते हैं, जो कि वे बनाए गए थे, न कि दायरे में। – GManNickG

+0

सबसे पहले, यह एक हास्यास्पद बयान है। दूसरा, इस मामले में संदर्भ इस व्यवहार को पूरी तरह से स्पष्ट नहीं करते हैं। लंग -1 – Stephen

0

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

यदि आप डिफ़ॉल्ट कन्स्ट्रक्टर का आह्वान करना चाहते हैं, तो यह कोई समस्या नहीं होनी चाहिए। बस कंटेनर के डिफ़ॉल्ट कन्स्ट्रक्टर का आह्वान करें।

यदि आप निहित प्रकार के गैर-डिफ़ॉल्ट कन्स्ट्रक्टर को आमंत्रित करना चाहते हैं तो यह शायद अधिक समस्याग्रस्त है। सी ++ 0 एक्स के लिए इसके लिए बेहतर समाधान होंगे।

एक व्यायाम के रूप में, कंटेनर टी के निर्माता के लिए तर्क युक्त टी, या ऑब्जेक्ट स्वीकार कर सकता है। यह अभी भी आरवीओ (रिटर्न वैल्यू ऑप्टिमाइज़ेशन) पर निर्भर करता है।

template <class T1> 
class construct_with_1 
{ 
    T1 _1; 
public: 
    construct_with_1(const T1& t1): _1(t1) {} 
    template <class U> 
    U construct() const { return U(_1); } 
}; 

template <class T1, class T2> 
class construct_with_2 
{ 
    T1 _1; 
    T2 _2; 
public: 
    construct_with_2(const T1& t1, const T2& t2): _1(t1), _2(t2) {} 
    template <class U> 
    U construct() const { return U(_1, _2); } 
}; 

//etc for other arities 

template <class T1> 
construct_with_1<T1> construct_with(const T1& t1) 
{ 
    return construct_with_1<T1>(t1); 
} 

template <class T1, class T2> 
construct_with_2<T1, T2> construct_with(const T1& t1, const T2& t2) 
{ 
    return construct_with_2<T1, T2>(t1, t2); 
} 

//etc 
template <class T> 
T construct(const T& source) { return source; } 

template <class T, class T1> 
T construct(const construct_with_1<T1>& args) 
{ 
    return args.template construct<T>(); 
} 

template <class T, class T1, class T2> 
T construct(const construct_with_2<T1, T2>& args) 
{ 
    return args.template construct<T>(); 
} 

template <class T> 
class Container 
{ 
public: 
    T first, second; 

    template <class T1, class T2> 
    Container(const T1& a = T1(), const T2& b = T2()) : 
     first(construct<T>(a)), second(construct<T>(b)) {} 
}; 

#include <iostream> 

class Test 
{ 
    int n; 
    double d; 
public: 
    Test(int a, double b = 0.0): n(a), d(b) { std::cout << "Test(" << a << ", " << b << ")\n"; } 
    Test(const Test& x): n(x.n), d(x.d) { std::cout << "Test(const Test&)\n"; } 
    void foo() const { std::cout << "Test.foo(" << n << ", " << d << ")\n"; } 
}; 

int main() 
{ 
    Test test(4, 3.14); 
    Container<Test> a(construct_with(1), test); //first constructed internally, second copied 
    a.first.foo(); 
    a.second.foo(); 
} 
संबंधित मुद्दे