एक कॉन्स्टेबल रेफरेंस क्लास मेंबर अस्थायी जीवन को लम्बा खींचता है?


171

ऐसा क्यों होता है:

#include <string>
#include <iostream>
using namespace std;

class Sandbox
{
public:
    Sandbox(const string& n) : member(n) {}
    const string& member;
};

int main()
{
    Sandbox sandbox(string("four"));
    cout << "The answer is: " << sandbox.member << endl;
    return 0;
}

का आउटपुट दें:

उत्तर है:

के बजाय:

जवाब है: चार


39
और बस और अधिक मज़े के लिए, अगर आपने लिखा था cout << "The answer is: " << Sandbox(string("four")).member << endl;, तो यह काम करने की गारंटी होगी।

7
@RogerPate क्या आप बता सकते हैं कि क्यों?
पाओलो एम

16
किसी ऐसे व्यक्ति के लिए जो जिज्ञासु है, उदाहरण के लिए रोजर पाटे ने काम किया क्योंकि स्ट्रिंग ("चार") अस्थायी है और यह कि अस्थायी पूरी तरह से समाप्त होने के बाद नष्ट हो जाती है , इसलिए उसके उदाहरण में जब SandBox::memberपढ़ा जाता है, तो अस्थायी स्ट्रिंग अभी भी जीवित है
PcAF

1
सवाल यह है: चूंकि ऐसी कक्षाएं लिखना खतरनाक है, क्या ऐसी कक्षाओं में अस्थायी लोगों को पास करने के खिलाफ एक संकलक चेतावनी है , या क्या एक डिज़ाइन गाइडलाइन है (स्ट्रॉस्ट्रुप में?) जो कि उन संदर्भों को संग्रहीत करने वाली कक्षाओं को लिखने से रोकती है? संदर्भों के बजाय पॉइंटर्स को स्टोर करने के लिए एक डिज़ाइन गाइडलाइन बेहतर होगी।
ग्रिम फैंडैंगो

@PAF: क्या आप बता सकते हैं कि string("four")पूर्ण अभिव्यक्ति के अंत में अस्थायी को क्यों नष्ट किया जाता है, और Sandboxनिर्माणकर्ता के बाहर निकलने के बाद नहीं ? पोटैटोस्वाटर का उत्तर कहता है कि एक कंस्ट्रक्टर के ctor-initializer ([12.6.2 [class.base.init]) में एक संदर्भ सदस्य के लिए एक अस्थायी बाध्य तब तक बनी रहती है जब तक कि कंस्ट्रक्टर बाहर नहीं निकल जाता है।
टेलर निकोल्स

जवाबों:


166

केवल स्थानीय const संदर्भ जीवनकाल को लम्बा खींचते हैं।

मानक standard8.5.3 / 5, [dcl.init.ref] में इस तरह के व्यवहार को निर्दिष्ट करता है, संदर्भ घोषणाओं के शुरुआती पर अनुभाग। आपके उदाहरण में संदर्भ कंस्ट्रक्टर के तर्क के लिए बाध्य है n, और ऑब्जेक्ट nके दायरे से बाहर जाने के लिए बाध्य होने पर अमान्य हो जाता है।

फ़ंक्शन तर्क के माध्यम से आजीवन विस्तार सकर्मक नहीं है। §12.2 / 5 [class.temporary]:

दूसरा संदर्भ तब होता है जब एक संदर्भ अस्थायी के लिए बाध्य होता है। अस्थायी जिसे संदर्भ बाध्य किया गया है या अस्थायी है जो एक उप-विषय के लिए पूरी वस्तु है जिसमें अस्थायी बाध्य है संदर्भ के जीवनकाल के लिए नीचे निर्दिष्ट के अलावा बनी रहती है। एक कंस्ट्रक्टर के ctor- इनिशियलाइज़र (.612.6.2 [class.base.init]) में एक संदर्भ सदस्य के लिए एक अस्थायी बन्धन तब तक बना रहता है जब तक कंस्ट्रक्टर बाहर नहीं निकलता। फ़ंक्शन कॉल (.25.2.2 [expr.call]) में एक संदर्भ पैरामीटर के लिए एक अस्थायी बाध्य कॉल को पूर्ण अभिव्यक्ति के पूरा होने तक बनी रहती है।


49
आपको अधिक मानव-अनुकूल स्पष्टीकरण के लिए GotW # 88 भी देखना चाहिए: जड़ी बूटियों की कड़ियाँ.com
नाथन अर्नस्ट

1
मुझे लगता है कि यह स्पष्ट होगा यदि मानक ने कहा "दूसरा संदर्भ तब है जब एक संदर्भ एक प्रचलन के लिए बाध्य है"। ओपी के कोड में आप कह सकते हैं कि memberयह एक अस्थायी के लिए बाध्य है, क्योंकि एक ही वस्तु से बांधने के लिए अर्थ के memberसाथ प्रारंभ करना बाध्य है, और यह वास्तव में इस मामले में एक अस्थायी वस्तु है। nmembern
MM

2
@MM ऐसे मामले हैं जहां एक प्रचलन वाले लवल्यू या ज़वल्यू इनिशियलाइज़र प्रील्यूव का विस्तार करेंगे। मेरा प्रस्ताव पत्र P0066 मामलों की स्थिति की समीक्षा करता है।
पोटाटोस्वाटर

1
C ++ 11 के अनुसार, रूवल्यू रेफरेंस को एक constक्वालीफायर की आवश्यकता के बिना एक अस्थायी जीवन को भी लम्बा खींचता है।
GetFree

3
@KeNVinFavo हाँ, एक मृत वस्तु का उपयोग करना हमेशा UB होता है
Potatoswatter

30

यह बताने का सबसे सरल तरीका है कि क्या हुआ:

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

यदि आप अपने इच्छित व्यवहार को प्रदर्शित करने के लिए अपना कार्यक्रम ठीक करना चाहते हैं, तो निम्न परिवर्तन करें:

int main()
{
    string temp = string("four");    
    Sandbox sandbox(temp);
    cout << sandbox.member << endl;
    return 0;
}

अब अस्थायी कार्यक्षेत्र के अंत में मुख्य () के अंत में दायरे से बाहर हो जाएगा। हालांकि, यह बुरा अभ्यास है। आपके सदस्य चर को उस चर के संदर्भ में कभी नहीं होना चाहिए जो उदाहरण के बाहर मौजूद है। व्यवहार में, आप कभी नहीं जानते कि कब वह चर दायरे से बाहर हो जाएगा।

मैं जो सलाह देता हूं वह सैंडबॉक्स के रूप में परिभाषित करना है const string member; यह अस्थायी पैरामीटर के डेटा को सदस्य पैरामीटर में अस्थायी पैरामीटर के रूप में ही असाइन करने के बजाय सदस्य चर में कॉपी करेगा।


अगर मैं ऐसा करता हूं: const string & temp = string("four"); Sandbox sandbox(temp); cout << sandbox.member << endl;क्या यह अभी भी काम करेगा?
यवेस

@ थोमस const string &temp = string("four");उसी तरह का परिणाम देता है const string temp("four"); , जब तक कि आप decltype(temp)विशेष रूप से उपयोग नहीं करते हैं
एमएम

@MM बहुत बहुत धन्यवाद अब मैं पूरी तरह से इस सवाल को समझता हूं।
यवेस

However, this is bad practice.- क्यों? यदि दोनों अस्थायी और युक्त वस्तु एक ही दायरे में स्वचालित भंडारण का उपयोग करते हैं, तो क्या यह 100% सुरक्षित नहीं है? और यदि आप ऐसा नहीं करते हैं, तो क्या होगा यदि स्ट्रिंग बहुत बड़ी है और इतनी महंगी है कि नकल करना महंगा है?
अधिकतम

2
@ मोम, क्योंकि वर्ग सही दायरे के लिए अस्थायी में पारित लागू नहीं करता है। इसका मतलब है कि एक दिन आप इस आवश्यकता के बारे में भूल गए, अमान्य अस्थायी मान पास कर सकते हैं और संकलक आपको चेतावनी नहीं देगा।
एलेक्स चे

5

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

  • cout << "The answer is: "बिट उत्सर्जित करेगा "The answer is: "में बफर stdout की।

  • फिर << sandbox.memberबिट झूलने वाले संदर्भ की आपूर्ति करेगा operator << (ostream &, const std::string &), जो अपरिभाषित व्यवहार को आमंत्रित करता है

इस वजह से, कुछ भी होने की गारंटी नहीं है। कार्यक्रम काफी हद तक ठीक काम कर सकता है या यहां तक ​​कि निस्तब्धता के बिना भी दुर्घटनाग्रस्त हो सकता है - जिसका अर्थ है "उत्तर है:" आपकी स्क्रीन पर प्रदर्शित होने के लिए नहीं मिलेगा।


2
जब यूबी होता है, तो पूरे कार्यक्रम का व्यवहार अपरिभाषित होता है - यह निष्पादन में किसी विशेष बिंदु पर शुरू नहीं होता है। इसलिए हम कुछ के लिए नहीं कह सकते हैं कि "The answer is: "कहीं भी लिखा जाएगा।
टोबी स्पाइट

0

क्योंकि सैंडबॉक्स कंस्ट्रक्टर के वापस आने के बाद आपका अस्थायी स्ट्रिंग कार्यक्षेत्र से बाहर चला गया था, और इसके द्वारा कब्जा कर लिया गया स्टैक कुछ अन्य उद्देश्यों के लिए पुनः प्राप्त किया गया था।

आम तौर पर, आपको कभी भी संदर्भ को दीर्घकालिक नहीं रखना चाहिए। संदर्भ तर्क या स्थानीय चर के लिए अच्छे हैं, कभी भी वर्ग के सदस्य नहीं हैं।


7
"कभी नहीं" एक भयानक रूप से मजबूत शब्द है।
फ्रेड लार्सन

17
जब तक आपको किसी वस्तु का संदर्भ रखने की आवश्यकता न हो, तब तक वर्ग के सदस्यों को कभी नहीं । ऐसे मामले हैं जहां आपको अन्य वस्तुओं के संदर्भों को रखने की आवश्यकता है, न कि प्रतियों की, उन मामलों के संदर्भों में संकेत की तुलना में एक स्पष्ट समाधान है।
डेविड रॉड्रिग्ज -

0

आप किसी ऐसी चीज़ का जिक्र कर रहे हैं जो गायब हो गई है। निम्नलिखित काम करेगा

#include <string>
#include <iostream>

class Sandbox
{

public:
    const string member = " "; //default to whatever is the requirement
    Sandbox(const string& n) : member(n) {}//a copy is made

};

int main()
{
    Sandbox sandbox(string("four"));
    std::cout << "The answer is: " << sandbox.member << std::endl;
    return 0;
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.