c++ - उनक - हिंदी मुहावरे और लोकोक्तियाँ




कॉपी-एंड-स्वैप मुहावरे क्या है? (4)

अवलोकन

हमें कॉपी-एंड-स्वैप मुहावरे की आवश्यकता क्यों है?

कोई भी वर्ग जो संसाधन प्रबंधित करता है (एक स्मार्ट पॉइंटर की तरह एक रैपर ) को द बिग थ्री को लागू करने की आवश्यकता होती है। जबकि कॉपी-कन्स्ट्रक्टर और विनाशक के लक्ष्य और कार्यान्वयन सरल हैं, प्रतिलिपि असाइनमेंट ऑपरेटर तर्कसंगत रूप से सबसे अधिक ज्ञात और कठिन है। यह कैसे किया जाना चाहिए? क्या नुकसान से बचा जाना चाहिए?

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

यह कैसे काम करता है?

Conceptually , यह डेटा की स्थानीय प्रतिलिपि बनाने के लिए कॉपी-कन्स्ट्रक्टर की कार्यक्षमता का उपयोग करके काम करता है, फिर प्रतिलिपि डेटा को एक swap फ़ंक्शन के साथ ले जाता है, पुराने डेटा को नए डेटा के साथ बदल देता है। अस्थायी प्रतिलिपि तब नष्ट हो जाती है, इसके साथ पुराना डेटा लेता है। हमें नए डेटा की एक प्रति के साथ छोड़ दिया गया है।

कॉपी-एंड-स्वैप मुहावरे का उपयोग करने के लिए, हमें तीन चीजों की आवश्यकता है: एक कार्यशील प्रति-निर्माता, एक कार्यरत विनाशक (दोनों किसी भी रैपर का आधार हैं, इसलिए वैसे भी पूरा होना चाहिए), और एक swap फ़ंक्शन।

एक स्वैप फ़ंक्शन एक गैर-फेंकने वाला फ़ंक्शन है जो वर्ग के दो ऑब्जेक्ट्स को सदस्य बनाता है, सदस्य के लिए सदस्य। हम अपने स्वयं के उपलब्ध कराने के बजाय std::swap का उपयोग करने के लिए लुभाने वाले हो सकते हैं, लेकिन यह असंभव होगा; std::swap प्रतिलिपि बनाने के भीतर कॉपी-कन्स्ट्रक्टर और कॉपी-असाइनमेंट ऑपरेटर का उपयोग करता है, और अंत में हम असाइनमेंट ऑपरेटर को स्वयं के संदर्भ में परिभाषित करने का प्रयास करेंगे!

(केवल इतना ही नहीं, लेकिन swap करने के लिए अयोग्य कॉल हमारे कस्टम स्वैप ऑपरेटर का उपयोग करेंगे, अनावश्यक निर्माण और हमारी कक्षा के विनाश को छोड़कर std::swap होगा।)

एक गहराई से स्पष्टीकरण

लक्ष्य

चलो एक ठोस मामला मानते हैं। हम एक अन्यथा बेकार वर्ग, एक गतिशील सरणी में, प्रबंधन करना चाहते हैं। हम एक कामकाजी कन्स्ट्रक्टर, प्रति-निर्माता, और विनाशक के साथ शुरू करते हैं:

#include <algorithm> // std::copy
#include <cstddef> // std::size_t

class dumb_array
{
public:
    // (default) constructor
    dumb_array(std::size_t size = 0)
        : mSize(size),
          mArray(mSize ? new int[mSize]() : nullptr)
    {
    }

    // copy-constructor
    dumb_array(const dumb_array& other)
        : mSize(other.mSize),
          mArray(mSize ? new int[mSize] : nullptr),
    {
        // note that this is non-throwing, because of the data
        // types being used; more attention to detail with regards
        // to exceptions must be given in a more general case, however
        std::copy(other.mArray, other.mArray + mSize, mArray);
    }

    // destructor
    ~dumb_array()
    {
        delete [] mArray;
    }

private:
    std::size_t mSize;
    int* mArray;
};

यह वर्ग लगभग सरणी को सफलतापूर्वक प्रबंधित करता है, लेकिन इसे सही ढंग से काम करने के लिए operator= आवश्यकता होती है।

एक असफल समाधान

यहां बताया गया है कि एक निष्पक्ष कार्यान्वयन कैसे देख सकता है:

// the hard part
dumb_array& operator=(const dumb_array& other)
{
    if (this != &other) // (1)
    {
        // get rid of the old data...
        delete [] mArray; // (2)
        mArray = nullptr; // (2) *(see footnote for rationale)

        // ...and put in the new
        mSize = other.mSize; // (3)
        mArray = mSize ? new int[mSize] : nullptr; // (3)
        std::copy(other.mArray, other.mArray + mSize, mArray); // (3)
    }

    return *this;
}

और हम कहते हैं कि हम समाप्त हो गए हैं; यह अब लीक के बिना, एक सरणी का प्रबंधन करता है। हालांकि, यह तीन समस्याओं से ग्रस्त है, क्रमशः कोड में (n) के रूप में चिह्नित किया गया है।

  1. पहला स्व-असाइनमेंट टेस्ट है। यह चेक दो उद्देश्यों को पूरा करता है: यह स्वयं को असाइनमेंट पर आवश्यक कोड चलाने से रोकने का एक आसान तरीका है, और यह हमें सूक्ष्म बग से बचाता है (जैसे सरणी को केवल कोशिश करने और कॉपी करने के लिए)। लेकिन अन्य सभी मामलों में यह केवल कार्यक्रम को धीमा करने और कोड में शोर के रूप में कार्य करने के लिए कार्य करता है; आत्म-असाइनमेंट शायद ही कभी होता है, इसलिए अधिकांश बार यह चेक अपशिष्ट होता है। यह बेहतर होगा अगर ऑपरेटर इसके बिना ठीक से काम कर सके।

  2. दूसरा यह है कि यह केवल एक बुनियादी अपवाद गारंटी प्रदान करता है। यदि new int[mSize] विफल रहता है, *this संशोधित किया जाएगा। (अर्थात्, आकार गलत है और डेटा चला गया है!) एक मजबूत अपवाद गारंटी के लिए, इसे कुछ ऐसा होना चाहिए:

    dumb_array& operator=(const dumb_array& other)
    {
        if (this != &other) // (1)
        {
            // get the new data ready before we replace the old
            std::size_t newSize = other.mSize;
            int* newArray = newSize ? new int[newSize]() : nullptr; // (3)
            std::copy(other.mArray, other.mArray + newSize, newArray); // (3)
    
            // replace the old data (all are non-throwing)
            delete [] mArray;
            mSize = newSize;
            mArray = newArray;
        }
    
        return *this;
    }
    
  3. कोड विस्तारित हो गया है! जो हमें तीसरी समस्या का कारण बनता है: कोड डुप्लिकेशन। हमारा असाइनमेंट ऑपरेटर प्रभावी रूप से अन्य कोड को डुप्लिकेट करता है जिसे हमने पहले ही लिखा है, और यह एक भयानक बात है।

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

(कोई आश्चर्यचकित हो सकता है: यदि एक संसाधन को सही ढंग से प्रबंधित करने के लिए यह कोड आवश्यक है, तो क्या होगा यदि मेरी कक्षा एक से अधिक का प्रबंधन करे? हालांकि यह एक वैध चिंता प्रतीत हो सकती है, और वास्तव में इसे गैर-तुच्छ try / catch क्लॉज try आवश्यकता होती है, यह है एक गैर-मुद्दा। ऐसा इसलिए है क्योंकि कक्षा को केवल एक संसाधन का प्रबंधन करना चाहिए!)

एक सफल समाधान

जैसा कि बताया गया है, कॉपी-एंड-स्वैप मुहावरे इन सभी मुद्दों को ठीक करेगा। लेकिन अभी, हमारे पास एक को छोड़कर सभी आवश्यकताएं हैं: एक swap फ़ंक्शन। जबकि नियम का तीन सफलतापूर्वक हमारे प्रति-निर्माता, असाइनमेंट ऑपरेटर और विनाशक के अस्तित्व में शामिल है, इसे वास्तव में "द बिग थ्री एंड ए हाफ" कहा जाना चाहिए: जब भी आपकी कक्षा संसाधन का प्रबंधन करती है तो यह भी swap प्रदान करने के लिए समझ में आता है समारोह।

हमें अपनी कक्षा में स्वैप कार्यक्षमता जोड़ने की जरूरत है, और हम निम्नानुसार करते हैं †:

class dumb_array
{
public:
    // ...

    friend void swap(dumb_array& first, dumb_array& second) // nothrow
    {
        // enable ADL (not necessary in our case, but good practice)
        using std::swap;

        // by swapping the members of two objects,
        // the two objects are effectively swapped
        swap(first.mSize, second.mSize);
        swap(first.mArray, second.mArray);
    }

    // ...
};

( Here स्पष्टीकरण दिया गया है कि क्यों public friend swap ।) अब न केवल हम अपने dumb_array स्वैप कर सकते हैं, लेकिन सामान्य रूप से स्वैप अधिक कुशल हो सकते हैं; यह केवल पूरे सरणी आवंटित करने और प्रतिलिपि बनाने के बजाय पॉइंटर्स और आकार को स्वैप करता है। कार्यक्षमता और दक्षता में इस बोनस के अलावा, अब हम कॉपी-एंड-स्वैप मुहावरे को लागू करने के लिए तैयार हैं।

आगे के बिना, हमारे असाइनमेंट ऑपरेटर है:

dumb_array& operator=(dumb_array other) // (1)
{
    swap(*this, other); // (2)

    return *this;
}

और बस! एक के साथ झुकाव के साथ, सभी तीन समस्याओं को एक बार में सुंदर ढंग से निपटाया जाता है।

यह क्यों काम करता है?

हम पहले एक महत्वपूर्ण विकल्प देखते हैं: पैरामीटर तर्क मूल्य के आधार पर लिया जाता है । जबकि कोई भी आसानी से निम्नलिखित कर सकता है (और वास्तव में, मुहावरे के कई बेवकूफ कार्यान्वयन):

dumb_array& operator=(const dumb_array& other)
{
    dumb_array temp(other);
    swap(*this, temp);

    return *this;
}

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

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

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

इस बिंदु पर हम घर से मुक्त हैं, क्योंकि swap गैर-फेंक रहा है। हम अपने वर्तमान डेटा को कॉपी किए गए डेटा के साथ स्वैप करते हैं, सुरक्षित रूप से हमारे राज्य को बदलते हैं, और पुराना डेटा अस्थायी में डाल दिया जाता है। जब समारोह वापस आता है तो पुराना डेटा तब जारी किया जाता है। (जहां पैरामीटर के दायरे समाप्त होते हैं और इसके विनाशक को बुलाया जाता है।)

चूंकि मुहावरे कोई कोड दोहराता है, हम ऑपरेटर के भीतर कीड़े पेश नहीं कर सकते हैं। ध्यान दें कि इसका मतलब है कि हम एक स्व-असाइनमेंट चेक की आवश्यकता से छुटकारा पा रहे हैं, जिससे operator= एक समान वर्दी कार्यान्वयन की अनुमति मिलती है। (इसके अतिरिक्त, अब हमारे पास गैर-कार्य-कार्य पर प्रदर्शन दंड नहीं है।)

और वह कॉपी-एंड-स्वैप मुहावरे है।

सी ++ 11 के बारे में क्या?

सी ++, सी ++ 11 का अगला संस्करण, संसाधनों को प्रबंधित करने के तरीके में एक बहुत ही महत्वपूर्ण परिवर्तन करता है: तीन का नियम अब चार (ढाई) का नियम है । क्यूं कर? क्योंकि न केवल हमें अपने संसाधन की प्रतिलिपि बनाने में सक्षम होने की आवश्यकता है , हमें इसे भी स्थानांतरित करने की आवश्यकता है

सौभाग्य से हमारे लिए, यह आसान है:

class dumb_array
{
public:
    // ...

    // move constructor
    dumb_array(dumb_array&& other)
        : dumb_array() // initialize via default constructor, C++11 only
    {
        swap(*this, other);
    }

    // ...
};

यहाँ क्या चल रहा है? चाल-निर्माण के लक्ष्य को याद करें: कक्षा के दूसरे उदाहरण से संसाधनों को लेने के लिए, इसे एक राज्य में छोड़कर असाइन करने योग्य और विनाशकारी होने की गारंटी दी जाती है।

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

(ध्यान दें कि कुछ कंपाइलर कन्स्ट्रक्टर प्रतिनिधिमंडल का समर्थन नहीं करते हैं; इस मामले में, हमें कक्षा को मैन्युअल रूप से डिफॉल्ट करना होगा। यह एक दुर्भाग्यपूर्ण लेकिन सौभाग्यपूर्ण मामूली कार्य है।)

वह क्यों काम करता है?

यही एकमात्र बदलाव है जिसे हमें अपनी कक्षा में बनाने की ज़रूरत है, तो यह क्यों काम करता है? पैरामीटर को एक मान बनाने के लिए किए गए हमेशा के लिए महत्वपूर्ण निर्णय याद रखें, संदर्भ नहीं:

dumb_array& operator=(dumb_array other); // (1)

अब, अगर other को एक रावल्यू के साथ शुरू किया जा रहा है, तो इसे स्थानांतरित किया जाएगा । उत्तम। उसी तरह सी ++ 03 हमें तर्क-मूल्य लेने के द्वारा हमारी कॉपी-कन्स्ट्रक्टर कार्यक्षमता का दोबारा उपयोग करने दें, सी ++ 11 स्वचालित रूप से उचित होने पर चालक-कन्स्ट्रक्टर को स्वचालित रूप से चुन देगा। (और, ज़ाहिर है, जैसा कि पहले से जुड़े आलेख में उल्लिखित है, मूल्य की प्रतिलिपि / गति को पूरी तरह से elided किया जा सकता है।)

और इसलिए कॉपी-एंड-स्वैप मुहावरे का निष्कर्ष निकाला जाता है।

फुटनोट

* हम mArray को शून्य पर क्यों सेट करते हैं? क्योंकि यदि ऑपरेटर में कोई और कोड फेंकता है, तो dumb_array के dumb_array को बुलाया जा सकता है; और यदि यह शून्य के बिना सेट किए बिना होता है, तो हम उस स्मृति को हटाने का प्रयास करते हैं जो पहले ही हटा दिया गया है! हम इसे शून्य से सेट करके इससे बचते हैं, क्योंकि शून्य हटाने से कोई ऑपरेशन नहीं होता है।

† अन्य दावों हैं कि हमें अपने प्रकार के लिए std::swap विशेषज्ञ होना चाहिए, एक फ्री-फ़ंक्शन swap इत्यादि के साथ एक इन-क्लास swap प्रदान करना चाहिए, लेकिन यह सब अनावश्यक है: swap का कोई भी उचित उपयोग अयोग्य के माध्यम से होगा कॉल करें, और हमारा कार्य ADL माध्यम से मिलेगा। एक समारोह करेगा।

‡ कारण सरल है: एक बार जब आपके पास संसाधन हो, तो आप इसे कहीं भी स्वैप कर सकते हैं और / या इसे स्थानांतरित कर सकते हैं (सी ++ 11) कहीं भी इसकी आवश्यकता होती है। और पैरामीटर सूची में प्रतिलिपि बनाकर, आप अनुकूलन को अधिकतम करते हैं।

यह मुहावरे क्या है और इसका उपयोग कब किया जाना चाहिए? यह कौन सी समस्याएं हल करती है? क्या सी ++ 11 का उपयोग होने पर मुहावरे बदल जाता है?

यद्यपि इसका उल्लेख कई स्थानों पर किया गया है, लेकिन हमारे पास कोई एकवचन "यह क्या है" प्रश्न और उत्तर नहीं था, इसलिए यहां यह है। यहां उन स्थानों की आंशिक सूची दी गई है जहां पहले उल्लेख किया गया था:


असाइनमेंट, उसके दिल पर, दो कदम है: वस्तु के पुराने राज्य को फाड़ना और किसी अन्य वस्तु के राज्य की प्रति के रूप में अपना नया राज्य बनाना

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

अपने परिष्कृत रूप में, प्रतिलिपि ऑपरेटर के (गैर-संदर्भ) पैरामीटर को प्रारंभ करके प्रतिलिपि बनाई गई प्रतिलिपि बनाकर कॉपी-एंड-स्वैप लागू किया जाता है:

T& operator=(T tmp)
{
    this->swap(tmp);
    return *this;
}

पहले से ही कुछ अच्छे जवाब हैं। मैं मुख्य रूप से उन चीज़ों पर ध्यान केंद्रित करूंगा जो मुझे लगता है कि उनकी कमी है - प्रतिलिपि और स्वैप मुहावरे के साथ "विपक्ष" का एक स्पष्टीकरण ....

कॉपी-एंड-स्वैप मुहावरे क्या है?

स्वैप फ़ंक्शन के संदर्भ में असाइनमेंट ऑपरेटर को कार्यान्वित करने का एक तरीका:

X& operator=(X rhs)
{
    swap(rhs);
    return *this;
}

मौलिक विचार यह है कि:

  • ऑब्जेक्ट को असाइन करने का सबसे त्रुटि-प्रवण हिस्सा यह सुनिश्चित करता है कि नए राज्य की जरूरतों को हासिल करने वाले किसी भी संसाधन को सुनिश्चित किया जाए (उदाहरण के लिए स्मृति, वर्णनकर्ता)

  • यदि ऑब्जेक्ट की वर्तमान स्थिति को संशोधित करने से पहले अधिग्रहण का प्रयास किया जा सकता है (यानी *this ) यदि नए मूल्य की एक प्रति बनाई गई है, तो यही कारण है कि rhs संदर्भ के बजाय मूल्य (यानी कॉपी किया गया) द्वारा स्वीकार किया जाता है

  • स्थानीय प्रतिलिपि rhs की स्थिति को स्वैप करना और *this आमतौर पर संभावित विफलता / अपवादों के बिना अपेक्षाकृत आसान है, स्थानीय प्रतिलिपि को बाद में किसी विशेष राज्य की आवश्यकता नहीं होती है (केवल विनाशक के लिए राज्य फिट की आवश्यकता होती है, जितना अधिक वस्तु => सी ++ 11 से स्थानांतरित हो रही है)

इसका इस्तेमाल कब किया जाना चाहिए? (कौन सी समस्याएं हल करती हैं [/ create] ?)

  • जब आप असाइनमेंट से असाइन किए गए असाइनमेंट को ऑब्जेक्ट करना चाहते हैं जो एक अपवाद फेंकता है, मानते हैं कि आपके पास मजबूत अपवाद गारंटी के साथ एक swap लिखना है या नहीं, और आदर्श रूप से जो विफल / throw नहीं सकता .. †

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

    • कॉपी-एंड-स्वैप के रूप में स्वयं-असाइनमेंट किए गए किनारे के मामलों से बचा जाता है। ‡

  • असाइनमेंट के दौरान अतिरिक्त अस्थायी ऑब्जेक्ट द्वारा बनाए गए किसी प्रदर्शन प्रदर्शन या क्षणिक रूप से उच्च संसाधन उपयोग आपके आवेदन के लिए महत्वपूर्ण नहीं है। ⁂

swap फेंकना: आमतौर पर डेटा सदस्यों को विश्वसनीय रूप से स्वैप करना संभव है कि ऑब्जेक्ट पॉइंटर द्वारा ट्रैक किए जाते हैं, लेकिन गैर-पॉइंटर डेटा सदस्यों जिनमें फेंक-फ्री स्वैप नहीं होता है, या जिसके लिए स्वैपिंग को X tmp = lhs; lhs = rhs; rhs = tmp; रूप में कार्यान्वित किया X tmp = lhs; lhs = rhs; rhs = tmp; X tmp = lhs; lhs = rhs; rhs = tmp; और प्रति-निर्माण या असाइनमेंट फेंक सकता है, फिर भी कुछ डेटा सदस्यों को स्वैप करने में विफल होने की संभावना है और अन्य नहीं। यह क्षमता किसी अन्य उत्तर पर जेम्स टिप्पणियों के रूप में सी ++ 03 std::string लिए भी लागू होती है:

@ विल्हेल्मटेल: सी ++ 03 में, std :: string :: swap (जिसे std :: swap द्वारा बुलाया जाता है) द्वारा संभावित रूप से फेंकने वाले अपवादों का कोई उल्लेख नहीं है। सी ++ 0x में, std :: string :: स्वैप अस्वीकार्य है और अपवाद फेंकना नहीं चाहिए। - जेम्स मैकनेलिस 22 दिसंबर 1010 को 15:24 बजे

‡ असाइनमेंट ऑपरेटर कार्यान्वयन जो किसी विशिष्ट ऑब्जेक्ट से असाइन करते समय सायन लगता है, वह स्वयं-असाइनमेंट के लिए आसानी से विफल हो सकता है। हालांकि यह अकल्पनीय प्रतीत हो सकता है कि क्लाइंट कोड स्वयं-असाइनमेंट का भी प्रयास करेगा, यह x = f(x); साथ कंटेनरों पर अल्गो संचालन के दौरान अपेक्षाकृत आसानी से हो सकता है x = f(x); कोड जहां f है (शायद केवल कुछ #ifdef शाखाओं के लिए) एक मैक्रो अला #define f(x) x या #define f(x) x संदर्भ में एक फ़ंक्शन लौटाता है, या यहां तक ​​कि (संभवतः अक्षम लेकिन संक्षिप्त) कोड x = c1 ? x * 2 : c2 ? x / 2 : x; जैसे कोड x = c1 ? x * 2 : c2 ? x / 2 : x; x = c1 ? x * 2 : c2 ? x / 2 : x; )। उदाहरण के लिए:

struct X
{
    T* p_;
    size_t size_;
    X& operator=(const X& rhs)
    {
        delete[] p_;  // OUCH!
        p_ = new T[size_ = rhs.size_];
        std::copy(p_, rhs.p_, rhs.p_ + rhs.size_);
    }
    ...
};

स्व-असाइनमेंट पर, उपर्युक्त कोड हटाएं x.p_; , एक नए आवंटित ढेर क्षेत्र में p_ को इंगित करता है, फिर उसमें अनियंत्रित डेटा को पढ़ने का प्रयास करता है (अपरिभाषित व्यवहार), यदि वह कुछ भी अजीब नहीं करता है, तो copy हर बस नष्ट 'टी' के लिए एक स्व-असाइनमेंट का प्रयास करती है!

⁂ प्रति-और-स्वैप मुहावरे अतिरिक्त अस्थायी (जब ऑपरेटर का पैरामीटर प्रति-निर्मित होता है) के उपयोग के कारण अक्षमता या सीमाएं पेश कर सकता है:

struct Client
{
    IP_Address ip_address_;
    int socket_;
    X(const X& rhs)
      : ip_address_(rhs.ip_address_), socket_(connect(rhs.ip_address_))
    { }
};

यहां, एक हाथ से लिखित Client::operator= यह जांच सकता है कि *this पहले से ही उसी सर्वर से जुड़ा हुआ है जैसे rhs (शायद उपयोगी होने पर "रीसेट" कोड भेजना), जबकि कॉपी-एंड-स्वैप दृष्टिकोण कॉपी- कन्स्ट्रक्टर जो संभवतः एक अलग सॉकेट कनेक्शन खोलने के लिए लिखा जाएगा, फिर मूल को बंद करें। न केवल एक साधारण इन-प्रोसेस वेरिएबल प्रतिलिपि के बजाय रिमोट नेटवर्क इंटरैक्शन का मतलब हो सकता है, यह सॉकेट संसाधनों या कनेक्शन पर क्लाइंट या सर्वर सीमाओं को दूर कर सकता है। (बेशक इस वर्ग में एक बहुत ही भयंकर इंटरफ़ेस है, लेकिन यह एक और मामला है; -पी)।


यह उत्तर उपरोक्त उत्तरों के लिए एक अतिरिक्त और मामूली संशोधन की तरह है।

विजुअल स्टूडियो (और संभवतः अन्य कंपाइलर्स) के कुछ संस्करणों में एक बग है जो वास्तव में परेशान है और समझ में नहीं आता है। इसलिए यदि आप इस तरह अपने swap फ़ंक्शन को घोषित / परिभाषित करते हैं:

friend void swap(A& first, A& second) {

    std::swap(first.size, second.size);
    std::swap(first.arr, second.arr);

}

... जब आप swap फ़ंक्शन को कॉल करते हैं तो संकलक आपको चिल्लाएगा:

इसका friend कार्य करने के साथ कुछ करना है और this वस्तु पैरामीटर के रूप में पारित की जा रही है।

इसके आस-पास एक तरीका friend कीवर्ड का उपयोग नहीं करना है और swap फ़ंक्शन को फिर से परिभाषित करना है:

void swap(A& other) {

    std::swap(size, other.size);
    std::swap(arr, other.arr);

}

इस बार, आप केवल swap कॉल कर सकते हैं और other में पास कर सकते हैं, इस प्रकार कंपाइलर को खुश कर सकते हैं:

आखिरकार, आपको 2 ऑब्जेक्ट्स को स्वैप करने के लिए किसी friend फ़ंक्शन का उपयोग करने की आवश्यकता नहीं है । यह एक सदस्य फ़ंक्शन को swap करने के लिए उतना ही समझ में आता है जिसमें पैरामीटर के रूप में एक other वस्तु होती है।

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





copy-and-swap