सी ++ में अस्थायी जीवनकाल की गारंटी?


103

क्या C ++ एक अस्थायी चर के जीवनकाल के लिए एक गारंटी प्रदान करता है जो एक फ़ंक्शन कॉल के भीतर बनाया गया है लेकिन एक पैरामीटर के रूप में उपयोग नहीं किया जाता है? यहाँ एक उदाहरण वर्ग है:

class StringBuffer
{
public:
    StringBuffer(std::string & str) : m_str(str)
    {
        m_buffer.push_back(0);
    }
    ~StringBuffer()
    {
        m_str = &m_buffer[0];
    }
    char * Size(int maxlength)
    {
        m_buffer.resize(maxlength + 1, 0);
        return &m_buffer[0];
    }
private:
    std::string & m_str;
    std::vector<char> m_buffer;
};

और यहां बताया गया है कि आप इसका उपयोग कैसे करेंगे:

// this is from a crusty old API that can't be changed
void GetString(char * str, int maxlength);

std::string mystring;
GetString(StringBuffer(mystring).Size(MAXLEN), MAXLEN);

अस्थायी StringBuffer ऑब्जेक्ट के लिए विनाशकारी कब कहा जाएगा? क्या यह:

  • GetString पर कॉल करने से पहले?
  • गेटस्ट्रिंग रिटर्न के बाद?
  • संकलक निर्भर?

मुझे पता है कि C ++ गारंटी देता है कि एक स्थानीय अस्थायी वैरिएबल तब तक मान्य होगा जब तक कि उसका कोई सन्दर्भ न हो - क्या यह माता-पिता की वस्तुओं पर लागू होता है जब एक सदस्य चर का संदर्भ होता है?

धन्यवाद।


क्यों नहीं विरासत और अधिभार या एक वैश्विक कार्य करते हैं? मैं साफ-सुथरा रहूंगा और आपको किसी सदस्य को बुलाने के लिए क्लास ओनियन नहीं बनाना पड़ेगा।
जसेक nowawrynowicz

1
आप इस का उपयोग करने जा रहे हैं, तो आप कॉल करना चाहिए m_str.reserve(maxlength)में char * Size(int maxlength), अन्यथा नाशक फेंक सकता है।
मकरसे

जवाबों:


109

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

यह वास्तव में क्या है जो अभिव्यक्ति टेम्पलेट्स को काम करता है: वे उस तरह के अस्थायी लोगों के संदर्भों को एक अभिव्यक्ति में रख सकते हैं जैसे

e = a + b * c / d

क्योंकि हर अस्थायी अभिव्यक्ति तक रहेगा

x = y

पूरी तरह से मूल्यांकन किया जाता है। यह 12.2 Temporary objectsमानक रूप से काफी संक्षिप्त रूप से वर्णित है ।


3
मैं मानक की एक प्रति पाने के लिए कभी नहीं मिला। मुझे इसे प्राथमिकता बनाना चाहिए।
मार्क फिरौती

2
@JohannesSchaub: इस मामले में "पूर्ण-अभिव्यक्ति" क्या है: printf("%s", strdup(std::string("$$$").c_str()) );मेरा मतलब है कि यदि strdup(std::string("$$$").c_str())पूर्ण अभिव्यक्ति के रूप में लिया जाता है, तो सूचक जो strdupदेखता है वह वैध है । यदि std::string("$$$").c_str()पूर्ण अभिव्यक्ति है, तो सूचक जो strdupदेखता है अमान्य है ! क्या आप इस उदाहरण के आधार पर कुछ और बता सकते हैं?
ग्रिम फैंडैंगो

2
@GrimFandango AIUI आपका पूरा printf अभिव्यक्ति है। इस प्रकार strdupयह एक अनावश्यक मेमोरी लीक है - आप इसे c_str()सीधे प्रिंट कर सकते हैं।
जोश स्टोन

1
"उस प्रकार के अस्थायी" - वह किस प्रकार का है? मैं यह कैसे बता सकता हूं कि मेरा अस्थायी "अस्थायी" है?
आरएम

@ आरएम मेला काफी मेरा मतलब था "जो आप एक फ़ंक्शन तर्क के भीतर बनाते हैं", जैसा कि प्रश्न में किया गया है।
जोहान्स शहाब -

18

litb का जवाब सटीक है। अस्थाई वस्तु का जीवनकाल (जिसे एक आवेश के रूप में भी जाना जाता है) को अभिव्यक्ति से जोड़ा जाता है और अस्थायी वस्तु के लिए विध्वंसक को पूर्ण अभिव्यक्ति के अंत में बुलाया जाता है और जब स्ट्रिंगबर्गर पर विध्वंसक कहा जाता है, तो m_buffer पर विध्वंसक भी होगा कहा जाता है, लेकिन m_str पर विनाशकारी नहीं है क्योंकि यह एक संदर्भ है।

ध्यान दें कि C ++ 0x चीजों को थोड़ा-थोड़ा बदल देता है क्योंकि यह रैवल्यू संदर्भ जोड़ता है और शब्दार्थ को स्थानांतरित करता है। अनिवार्य रूप से एक रेवल्यू रेफरेंस पैरामीटर (&& के साथ नॉटेड) का उपयोग करके मैं फंक्शन में रिवैल्यू को 'मूव' कर सकता हूं (इसके बजाय इसे कॉपी कर सकता हूं) और रिवेल्यू का जीवनकाल उस ऑब्जेक्ट के लिए बाध्य हो सकता है जो इसमें मूव करता है, एक्सप्रेशन नहीं। MSVC टीम का एक बहुत अच्छा ब्लॉग पोस्ट है जो इस पर विस्तार से चलता है और मैं लोगों को इसे पढ़ने के लिए प्रोत्साहित करता हूं।

गतिमान गति के लिए शैक्षणिक उदाहरण अस्थायी तार हैं और मैं एक निर्माणकर्ता में असाइनमेंट दिखाऊंगा। यदि मेरे पास एक वर्ग MyType है जिसमें एक स्ट्रिंग सदस्य चर है, तो इसे इस प्रकार बनाया जा सकता है:

class MyType{
   const std::string m_name;
public:
   MyType(const std::string&& name):m_name(name){};
}

यह अच्छा है क्योंकि जब मैं एक अस्थायी वस्तु के साथ इस वर्ग का उदाहरण घोषित करता हूं:

void foo(){
    MyType instance("hello");
}

क्या होता है कि हम अस्थायी ऑब्जेक्ट को कॉपी और नष्ट करने से बचते हैं और "हैलो" को सीधे मालिक वर्ग के सदस्य चर के अंदर रखा जाता है। यदि वस्तु एक 'स्ट्रिंग' की तुलना में भारी है, तो अतिरिक्त प्रतिलिपि और विध्वंसक कॉल महत्वपूर्ण हो सकता है।


1
चाल काम करने के लिए मुझे लगता है कि आपको कॉन्स्ट को छोड़ने और std का उपयोग करने की आवश्यकता है :: MyType (std :: string && name): m_name (std :: move (name)) {}
gast128


3

StringBuffer GetString के दायरे में है। यह गेटस्ट्रिंग के दायरे के अंत में नष्ट हो जाना चाहिए (यानी जब यह वापस लौटता है)। इसके अलावा, मुझे विश्वास नहीं है कि C ++ गारंटी देगा कि एक चर तब तक मौजूद रहेगा जब तक कि संदर्भ है।

निम्नलिखित को संकलित करना चाहिए:

Object* obj = new Object;
Object& ref = &(*obj);
delete obj;

मुझे लगता है कि मैंने गारंटी को समाप्त कर दिया है - यह केवल स्थानीय अस्थायी लोगों के लिए है। लेकिन यह मौजूद है।
मार्क रैनसम

मैंने प्रश्न संपादित किया है। इस प्रकार अब तक आपूर्ति किए गए उत्तरों के आधार पर, यह एक मूक बिंदु लगता है।
मार्क रैनसम

मुझे अभी भी नहीं लगता कि आपका संपादन सही है: ऑब्जेक्ट और obj = GetObj (); ऑब्जेक्ट और GetObj () {वापसी और ऑब्जेक्ट (); } // खराब - झूलने के संदर्भ छोड़ देंगे।
बिगसैंडविच

1
मैं स्पष्ट रूप से खुद को समझाने का बुरा काम कर रहा हूं, और मैं शायद 100% भी नहीं समझ पा रहा हूं। को देखो informit.com/guides/content.aspx?g=cplusplus&seqNum=198 - यह बताता है और साथ ही अपने मूल सवाल का जवाब।
मार्क रैनसम

1
धन्यवाद, लिंक के लिए, जो अब समझ में आता है।
बिगसैंडविच

3

मैंने लगभग उसी कक्षा को लिखा:

template <class C>
class _StringBuffer
{
    typename std::basic_string<C> &m_str;
    typename std::vector<C> m_buffer;

public:
    _StringBuffer(std::basic_string<C> &str, size_t nSize)
        : m_str(str), m_buffer(nSize + 1) { get()[nSize] = (C)0; }

    ~_StringBuffer()
        { commit(); }

    C *get()
        { return &(m_buffer[0]); }

    operator C *()
        { return get(); }

    void commit()
    {
        if (m_buffer.size() != 0)
        {
            size_t l = std::char_traits<C>::length(get());
            m_str.assign(get(), l);    
            m_buffer.resize(0);
        }
    }

    void abort()
        { m_buffer.resize(0); }
};

template <class C>
inline _StringBuffer<C> StringBuffer(typename std::basic_string<C> &str, size_t nSize)
    { return _StringBuffer<C>(str, nSize); }

मानक से पहले प्रत्येक संकलक ने इसे अलग तरीके से किया। मेरा मानना ​​है कि C ++ के लिए पुराने एनोटेटेड रेफरेंस मैनुअल ने निर्दिष्ट किया है कि अस्थायी लोगों को दायरे के अंत में सफाई करनी चाहिए, इसलिए कुछ कंपाइलरों ने ऐसा किया। 2003 के उत्तरार्ध में, मैंने पाया कि सन के फोर्टेस सी ++ कंपाइलर पर डिफ़ॉल्ट रूप से व्यवहार अभी भी मौजूद है, इसलिए स्ट्रिंगबफ़र ने काम नहीं किया। लेकिन अगर कोई करंट कंपाइलर अभी भी टूटा हुआ था, तो मुझे आश्चर्य होगा।


डरावना कैसे वे समान हैं! चेतावनी के लिए धन्यवाद - पहली जगह मैं कोशिश करूँगा कि यह वीसी ++ 6 है, जो इसके मानकों के अनुपालन के लिए नहीं जाना जाता है। मैं ध्यान से देख रहा हूँ।
मार्क रैनसम

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