c++ - क्या कॉपी-और-स्वैप आईडीईम सी++ 11 में कॉपी-और-मूड आईडीआईएम बन जाए?




सी++ प्रोग्राम्स (2)

जब मैंने इस सवाल से पूछा, तब से यह बहुत समय हो गया है, और मुझे थोड़ी देर के लिए जवाब पता है, लेकिन मैंने इसके लिए जवाब लिखना बंद कर दिया है। यही पर है।

जवाब न है। कॉपी-और-स्वैप मुहावरे कॉपी-और-कदम मुहावरे नहीं बनना चाहिए।

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

स्वैप व्यवहार नाशक का पुन: उपयोग करने में सक्षम होने के लिए है, इसलिए आपको अपने असाइनमेंट ऑपरेटरों में कोई क्लीनअप कोड लिखना नहीं है।

अगर कोई साफ-सफाई व्यवहार नहीं किया जाता है और केवल असाइनमेंट किया गया है, तो आपको असाइनमेंट ऑपरेटर को डिफ़ॉल्ट के रूप में घोषित करने में सक्षम होना चाहिए और कॉपी-और-स्वैप की आवश्यकता नहीं है।

चालन निर्माता को आमतौर पर किसी भी क्लीन अप व्यवहार की आवश्यकता नहीं होती, क्योंकि यह एक नई वस्तु है सामान्य सरल तरीका यह है कि चालन निर्माता डिफ़ॉल्ट कन्स्ट्रक्टर का आह्वान करता है, और फिर सभी सदस्यों को चाल-से-ऑब्जेक्ट के साथ स्वैप करता है। चले गए ऑब्जेक्ट तब एक ब्लेंड डिफ़ॉल्ट-निर्मित ऑब्जेक्ट की तरह होगा।

हालांकि, इस प्रश्न के पर्यवेक्षक पैटर्न उदाहरण में, यह वास्तव में एक अपवाद है जहां आपको अतिरिक्त सफाई काम करना पड़ता है क्योंकि पुराने ऑब्जेक्ट के संदर्भ में बदलाव की आवश्यकता है सामान्य तौर पर, मैं आपके पर्यवेक्षक और अवलोकन करने की सलाह देता हूं, और अन्य संदर्भों के आधार पर अन्य डिजाइन तैयार किए जाते हैं, जब भी संभव हो, जब तक संभव न हो।

जैसा कि इस उत्तर में बताया गया है, कॉपी-और-स्वैप मुहावरे निम्नानुसार लागू किया गया है:

class MyClass
{
private:
    BigClass data;
    UnmovableClass *dataPtr;

public:
    MyClass()
      : data(), dataPtr(new UnmovableClass) { }
    MyClass(const MyClass& other)
      : data(other.data), dataPtr(new UnmovableClass(*other.dataPtr)) { }
    MyClass(MyClass&& other)
      : data(std::move(other.data)), dataPtr(other.dataPtr)
    { other.dataPtr= nullptr; }

    ~MyClass() { delete dataPtr; }

    friend void swap(MyClass& first, MyClass& second)
    {
        using std::swap;
        swap(first.data, other.data);
        swap(first.dataPtr, other.dataPtr);
    }

    MyClass& operator=(MyClass other)
    {
        swap(*this, other);
        return *this;
    }
};

ऑपरेटर = के लिए पैरामीटर के रूप में MyClass का मान करके, पैरामीटर को प्रतिलिपि कन्स्ट्रक्टर या चालन निर्माता द्वारा या तो बनाया जा सकता है। फिर आप पैरामीटर से डेटा सुरक्षित रूप से निकाल सकते हैं। यह कोड दोहराव को रोकता है और अपवाद सुरक्षा में सहायता करता है।

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

इस अधिक जटिल उदाहरण पर गौर करें, जिसमें पर्यवेक्षक पैटर्न शामिल है । इस उदाहरण में, मैंने असाइनमेंट ऑपरेटर कोड मैन्युअल रूप से लिखा है I कदम चालन निर्माता, असाइनमेंट ऑपरेटर और स्वैप पद्धति पर जोर है:

class MyClass : Observable::IObserver
{
private:
    std::shared_ptr<Observable> observable;

public:
    MyClass(std::shared_ptr<Observable> observable) : observable(observable){ observable->registerObserver(*this); }
    MyClass(const MyClass& other) : observable(other.observable) { observable.registerObserver(*this); }
    ~MyClass() { if(observable != nullptr) { observable->unregisterObserver(*this); }}

    MyClass(MyClass&& other) : observable(std::move(other.observable))
    {
        observable->unregisterObserver(other);
        other.observable.reset(nullptr);
        observable->registerObserver(*this);
    }

    friend void swap(MyClass& first, MyClass& second)
    {
        //Checks for nullptr and same observable omitted
            using std::swap;
            swap(first.observable, second.observable);

            second.observable->unregisterObserver(first);
            first.observable->registerObserver(first);
            first.observable->unregisterObserver(second);
            second.observable->registerObserver(second);
    }

    MyClass& operator=(MyClass other)
    {
        observable->unregisterObserver(*this);
        observable = std::move(other.observable);

        observable->unregisterObserver(other);
        other.observable.reset(nullptr);
        observable->registerObserver(*this);
    }
}

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

क्या यह चाल रचनाकार के कोड को फिर से इस्तेमाल करने के लिए अधिक मायने नहीं रखेगा?

private:
    void performMoveActions(MyClass&& other)
    {
        observable->unregisterObserver(other);
        other.observable.reset(nullptr);
        observable->registerObserver(*this);
    }

public:
    MyClass(MyClass&& other) : observable(std::move(other.observable))
    {
        performMoveActions(other);
    }

    MyClass& operator=(MyClass other)
    {
        observable->unregisterObserver(*this);
        observable = std::move(other.observable);

        performMoveActions(other);
    }

यह मेरे लिए ऐसा लगता है कि इस दृष्टिकोण स्वैप दृष्टिकोण से नीच नहीं है। क्या मैं सोच रहा हूं कि प्रतिलिपि और स्वैप मुहावरा सी ++ 11 में कॉपी-और-कदम मुहावरों के मुकाबले बेहतर होगा, या मैंने कुछ महत्वपूर्ण याद किया?


प्रत्येक विशेष सदस्य को निविदा को प्यार करने योग्य देखभाल दें, और जितना संभव हो उतना उन्हें डिफॉल्ट करने का प्रयास करें:

class MyClass
{
private:
    BigClass data;
    std::unique_ptr<UnmovableClass> dataPtr;

public:
    MyClass() = default;
    ~MyClass() = default;
    MyClass(const MyClass& other)
        : data(other.data)
        , dataPtr(other.dataPtr ? new UnmovableClass(*other.dataPtr)
                                : nullptr)
        { }
    MyClass& operator=(const MyClass& other)
    {
        if (this != &other)
        {
            data = other.data;
            dataPtr.reset(other.dataPtr ? new UnmovableClass(*other.dataPtr)
                                        : nullptr);
        }
        return *this;
    }
    MyClass(MyClass&&) = default;
    MyClass& operator=(MyClass&&) = default;

    friend void swap(MyClass& first, MyClass& second)
    {
        using std::swap;
        swap(first.data, second.data);
        swap(first.dataPtr, second.dataPtr);
    }
};

यदि वांछित हो तो नाशकर्ता को ऊपर से निस्तारण किया जा सकता है इस उदाहरण के लिए अन्य सभी को स्पष्ट रूप से परिभाषित या डिफॉल्ट करना होगा।

संदर्भ: http://accu.org/content/conf2014/Howard_Hinnant_Accu_2014.pdf

कॉपी / स्वैप मुहावरे की संभावना आपके प्रदर्शन की संभावना (स्लाइड देखें)। उदाहरण के लिए कभी आश्चर्य न करें कि उच्च प्रदर्शन / अक्सर std :: types जैसे std::vector और std::string उपयोग प्रतिलिपि / स्वैप का उपयोग नहीं करते हैं? खराब प्रदर्शन का कारण है यदि BigClass में कोई भी std::vector या std::string एस (जो संभवतः लगता है) शामिल है, तो आपका सर्वश्रेष्ठ शर्त अपने विशेष सदस्यों से अपने विशेष सदस्यों को कॉल करना है। ऊपर यह है कि कैसे करना है।

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





copy-and-swap