सवाल तीन का नियम क्या है?


  • क्या करता है एक वस्तु की प्रतिलिपि बनाना क्या मतलब है?
  • वह क्या हैं प्रतिलिपि निर्माता और यह असाइनमेंट ऑपरेटर कॉपी करें?
  • मुझे उन्हें खुद घोषित करने की आवश्यकता कब होगी?
  • मैं अपनी वस्तुओं को कॉपी करने से कैसे रोक सकता हूं?

1844
2017-11-13 13:27


मूल


कृप्या पढ़ना यह पूरा धागा तथा c++-faq विकी टैग करें इससे पहले कि आप बंद करने के लिए वोट दें। - sbi
@ बाइनरी: कम से कम टिप्पणी चर्चा पढ़ने के लिए समय ले लो से पहले आपने वोट डाला पाठ बहुत आसान था, लेकिन फ्रेड को उस पर विस्तार करने के लिए कहा गया था। इसके अलावा, जबकि यह चार प्रश्न हैं व्याकरण की दृष्टि से, यह वास्तव में इसके कई पहलुओं के साथ सिर्फ एक प्रश्न है। (यदि आप इससे असहमत हैं, तो अपने प्रत्येक प्रश्न का उत्तर देकर अपने पीओवी को साबित करें और हमें परिणामों पर वोट दें।) - sbi
फ्रेड, सी ++ 1x के संबंध में आपके उत्तर में एक दिलचस्प जोड़ है: stackoverflow.com/questions/4782757/...। हम इससे कैसे निपट सकते हैं? - sbi
सम्बंधित: बिग टू का कानून - Nemanja Trifunovic
ध्यान रखें कि, सी ++ 11 के रूप में, मुझे लगता है कि इसे पांच के नियम में अपग्रेड कर दिया गया है, या ऐसा कुछ। - paxdiablo


जवाब:


परिचय

सी ++ उपयोगकर्ता परिभाषित प्रकार के चर के साथ व्यवहार करता है मूल्य अर्थशास्त्र। इसका मतलब है कि वस्तुओं को विभिन्न संदर्भों में पूरी तरह से कॉपी किया गया है, और हमें समझना चाहिए कि "ऑब्जेक्ट की प्रतिलिपि बनाना" वास्तव में क्या है।

आइए एक साधारण उदाहरण पर विचार करें:

class person
{
    std::string name;
    int age;

public:

    person(const std::string& name, int age) : name(name), age(age)
    {
    }
};

int main()
{
    person a("Bjarne Stroustrup", 60);
    person b(a);   // What happens here?
    b = a;         // And here?
}

(यदि आप द्वारा परेशान हैं name(name), age(age) अंश, इसे एक कहा जाता है सदस्य प्रारंभकर्ता सूची।)

विशेष सदस्य कार्य

एक प्रतिलिपि बनाने का क्या मतलब है person आपत्ति? main समारोह दो अलग प्रतिलिपि परिदृश्य दिखाता है। प्रारंभिकरण person b(a); द्वारा किया जाता है प्रतिलिपि निर्माता। इसका काम मौजूदा वस्तु की स्थिति के आधार पर एक ताजा वस्तु बनाना है। काम b = a द्वारा किया जाता है असाइनमेंट ऑपरेटर कॉपी करें। इसका काम आम तौर पर थोड़ा और जटिल है, क्योंकि लक्ष्य वस्तु पहले से ही कुछ मान्य राज्य में है जिसके साथ निपटा जाना आवश्यक है।

चूंकि हमने न तो कॉपी कन्स्ट्रक्टर और न ही असाइनमेंट ऑपरेटर (न ही विनाशक) घोषित किया है, ये हमारे लिए निहित रूप से परिभाषित हैं। मानक से उद्धरण:

[...] प्रतिलिपि निर्माता और कॉपी असाइनमेंट ऑपरेटर, [...] और विनाशक विशेष सदस्य कार्य हैं।   [ ध्यान दें: कार्यान्वयन इन सदस्य कार्यों को स्पष्ट रूप से घोषित करेगा   कुछ वर्ग प्रकारों के लिए जब कार्यक्रम स्पष्ट रूप से उन्हें घोषित नहीं करता है।   अगर उनका उपयोग किया जाता है तो कार्यान्वयन उन्हें निश्चित रूप से परिभाषित करेगा। [...] अंत नोट ]   [n3126.pdf सेक्शन 12 §1]

डिफ़ॉल्ट रूप से, किसी ऑब्जेक्ट की प्रतिलिपि बनाने का अर्थ है अपने सदस्यों की प्रतिलिपि बनाना:

गैर-यूनियन क्लास एक्स के लिए अंतर्निहित रूप से परिभाषित प्रतिलिपि निर्माता अपने उप-प्रोजेक्ट की सदस्यवृत्ति प्रतिलिपि करता है।   [n3126.pdf अनुभाग 12.8 §16]

एक गैर-यूनियन कक्षा एक्स के लिए अंतर्निहित परिभाषित प्रति असाइनमेंट ऑपरेटर सदस्यवार प्रति असाइनमेंट करता है   इसके उपनिवेशों का।   [n3126.pdf सेक्शन 12.8 §30]

लागू परिभाषाएं

अंतर्निहित परिभाषित विशेष सदस्य के लिए काम करता है person ऐसे दिखते हैं:

// 1. copy constructor
person(const person& that) : name(that.name), age(that.age)
{
}

// 2. copy assignment operator
person& operator=(const person& that)
{
    name = that.name;
    age = that.age;
    return *this;
}

// 3. destructor
~person()
{
}

सदस्यवार प्रतिलिपि ठीक वही है जो हम इस मामले में चाहते हैं: name तथा age प्रतिलिपि बनाई गई है, इसलिए हमें एक आत्मनिर्भर, स्वतंत्र मिलता है person वस्तु। निहित रूप से परिभाषित विनाशक हमेशा खाली रहता है। यह इस मामले में भी ठीक है क्योंकि हमने निर्माता में कोई संसाधन नहीं प्राप्त किया है। सदस्यों के विनाशकों को पूरी तरह से बुलाया जाता है person विनाशक खत्म हो गया है:

विनाशक के शरीर को निष्पादित करने और शरीर के भीतर आवंटित किसी भी स्वचालित वस्तुओं को नष्ट करने के बाद,   कक्षा एक्स के लिए एक विनाशक एक्स के प्रत्यक्ष [...] सदस्यों के लिए विनाशकों को बुलाता है   [n3126.pdf 12.4 §6]

संसाधनों का प्रबंधन

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

आइए प्री-स्टैंडर्ड सी ++ में समय पर वापस जाएं। ऐसी कोई चीज़ नहीं थी std::string, और प्रोग्रामर पॉइंटर्स के साथ प्यार में थे। person वर्ग इस तरह दिख सकता है:

class person
{
    char* name;
    int age;

public:

    // the constructor acquires a resource:
    // in this case, dynamic memory obtained via new[]
    person(const char* the_name, int the_age)
    {
        name = new char[strlen(the_name) + 1];
        strcpy(name, the_name);
        age = the_age;
    }

    // the destructor must release this resource via delete[]
    ~person()
    {
        delete[] name;
    }
};

आज भी, लोग अभी भी इस शैली में कक्षाएं लिखते हैं और परेशानी में पड़ते हैं: "मैंने एक व्यक्ति को वेक्टर में धक्का दिया और अब मुझे पागल स्मृति त्रुटियां मिलती हैं!" याद रखें कि डिफ़ॉल्ट रूप से, ऑब्जेक्ट की प्रतिलिपि बनाने का अर्थ है अपने सदस्यों की प्रतिलिपि बनाना, लेकिन कॉपी कर रहा है name सदस्य केवल एक सूचक की प्रतिलिपि बनाता है, नहीं चरित्र सरणी यह ​​इंगित करता है! इसमें कई अप्रिय प्रभाव हैं:

  1. के माध्यम से परिवर्तन a के माध्यम से देखा जा सकता है b
  2. एक बार b नष्ट हो चुका है, a.name एक लापरवाही सूचक है।
  3. अगर a खतरनाक सूचक पैदावार को नष्ट कर नष्ट कर दिया गया है अपरिभाषित व्यवहार
  4. चूंकि असाइनमेंट खाते में ध्यान नहीं देता है name असाइनमेंट से पहले इंगित किया, जल्दी या बाद में आप जगह पर मेमोरी लीक प्राप्त करेंगे।

स्पष्ट परिभाषाएं

चूंकि सदस्यवाही प्रतिलिपि के पास वांछित प्रभाव नहीं है, इसलिए हमें चरित्र सरणी की गहरी प्रतियां बनाने के लिए कॉपी कन्स्ट्रक्टर और कॉपी असाइनमेंट ऑपरेटर को स्पष्ट रूप से परिभाषित करना होगा:

// 1. copy constructor
person(const person& that)
{
    name = new char[strlen(that.name) + 1];
    strcpy(name, that.name);
    age = that.age;
}

// 2. copy assignment operator
person& operator=(const person& that)
{
    if (this != &that)
    {
        delete[] name;
        // This is a dangerous point in the flow of execution!
        // We have temporarily invalidated the class invariants,
        // and the next statement might throw an exception,
        // leaving the object in an invalid state :(
        name = new char[strlen(that.name) + 1];
        strcpy(name, that.name);
        age = that.age;
    }
    return *this;
}

प्रारंभिकरण और असाइनमेंट के बीच अंतर ध्यान दें: हमें असाइन करने से पहले पुराने राज्य को फाड़ना होगा name स्मृति रिसाव को रोकने के लिए। इसके अलावा, हमें फॉर्म के आत्म-कार्य के खिलाफ सुरक्षा करना है x = x। उस जांच के बिना, delete[] name जिसमें सरणी को हटा दिया जाएगा स्रोत स्ट्रिंग, क्योंकि जब आप लिखते हैं x = x, दोनों this->name तथा that.name एक ही सूचक शामिल है।

अपवाद सुरक्षा

दुर्भाग्य से, यह समाधान विफल हो जाएगा अगर new char[...] स्मृति थकावट के कारण अपवाद फेंकता है। एक संभावित समाधान स्थानीय चर को पेश करना और बयानों को पुन: व्यवस्थित करना है:

// 2. copy assignment operator
person& operator=(const person& that)
{
    char* local_name = new char[strlen(that.name) + 1];
    // If the above statement throws,
    // the object is still in the same state as before.
    // None of the following statements will throw an exception :)
    strcpy(local_name, that.name);
    delete[] name;
    name = local_name;
    age = that.age;
    return *this;
}

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

अपरिवर्तनीय संसाधन

कुछ संसाधन कॉपी नहीं किए जा सकते हैं या नहीं, जैसे फाइल हैंडल या म्यूटेक्स। उस स्थिति में, बस कॉपी कन्स्ट्रक्टर घोषित करें और असाइनमेंट ऑपरेटर कॉपी करें private परिभाषा दिए बिना:

private:

    person(const person& that);
    person& operator=(const person& that);

वैकल्पिक रूप से, आप से प्राप्त कर सकते हैं boost::noncopyable या उन्हें हटाए गए घोषित करें (सी ++ 0x):

person(const person& that) = delete;
person& operator=(const person& that) = delete;

तीन का शासन

कभी-कभी आपको एक ऐसी कक्षा को लागू करने की आवश्यकता होती है जो संसाधन का प्रबंधन करे। (कभी भी एक वर्ग में एकाधिक संसाधनों का प्रबंधन न करें, यह केवल दर्द का कारण बन जाएगा।) उस मामले में, याद रखें तीन का शासन:

यदि आपको या तो विनाशक घोषित करने की आवश्यकता है,   कॉपी कन्स्ट्रक्टर या कॉपी असाइनमेंट ऑपरेटर खुद को कॉपी करें,   आपको शायद उन सभी तीनों को स्पष्ट रूप से घोषित करने की आवश्यकता है।

(दुर्भाग्यवश, यह "नियम" सी ++ मानक या किसी भी कंपाइलर द्वारा लागू नहीं है जिसे मैं जानता हूं।)

सलाह

अधिकांश समय, आपको स्वयं संसाधन को प्रबंधित करने की आवश्यकता नहीं होती है, क्योंकि एक मौजूदा वर्ग जैसे std::string पहले से ही यह आपके लिए करता है। बस एक का उपयोग कर सरल कोड की तुलना करें std::string सदस्य एक का उपयोग कर घुलनशील और त्रुटि प्रवण विकल्प के लिए char* और आपको आश्वस्त होना चाहिए। जब तक आप कच्चे सूचक सदस्यों से दूर रहें, तब तक तीनों का नियम आपके स्वयं के कोड से संबंधित होने की संभावना नहीं है।


1517
2017-11-13 13:27



फ्रेड, मैं अपने अप-वोट के बारे में बेहतर महसूस करूंगा यदि (ए) आप कॉपी करने योग्य कोड में बुरी तरह कार्यान्वित असाइनमेंट का जादू नहीं करेंगे और एक नोट जोड़कर यह गलत है और जुर्माना में कहीं और देखें; या तो कोड में सी एंड एस का उपयोग करें या इन सभी सदस्यों को लागू करने पर छोड़ दें (बी) आप पहली छमाही को कम करेंगे, जो आरओटी के साथ बहुत कम नहीं है; (सी) आप चाल semantics के परिचय और RoT के लिए इसका मतलब क्या है पर चर्चा करेंगे। - sbi
लेकिन फिर मुझे लगता है कि पोस्ट सी / डब्ल्यू बनाया जाना चाहिए। मुझे लगता है कि आप शब्दों को अधिक सटीक रखते हैं (यानी आप कहते हैं "प्रतिलिपि असाइनमेंट ऑपरेटर ", और यह कि आप सामान्य जाल में टैप नहीं करते हैं कि असाइनमेंट एक प्रतिलिपि नहीं दे सकता है)। - Johannes Schaub - litb
@P्रासून: मुझे नहीं लगता कि उत्तर का आधा हिस्सा गैर-सीडब्ल्यू उत्तर के "उचित संपादन" के रूप में देखा जाएगा। - sbi
इसके अलावा, मैंने इसे ओवरड्रेड कर दिया होगा, लेकिन आप इस बात का जिक्र नहीं करते हैं कि प्रतिलिपि बनाने वाले ऑपरेटर को कुछ भी करने से पहले पहचान की जांच करनी चाहिए। - Björn Pollex
यह अच्छा होगा अगर आप सी ++ 11 के लिए अपनी पोस्ट अपडेट करते हैं (यानी कन्स्ट्रक्टर / असाइनमेंट ले जाएं) - Alexander Malakhov


तीन का नियम मूल रूप से कह रहे हैं, सी ++ के लिए अंगूठे का नियम है

अगर आपकी कक्षा में से किसी की जरूरत है

  • प्रतिलिपि निर्माता,
  • एक असाइनमेंट ऑपरेटर,
  • या ए नाशक,

स्पष्ट रूप से परिभाषित किया गया है, तो इसकी आवश्यकता होने की संभावना है उनमें से तीनों

इसके कारण यह है कि उनमें से तीन आमतौर पर संसाधन का प्रबंधन करने के लिए उपयोग किए जाते हैं, और यदि आपकी कक्षा संसाधन का प्रबंधन करती है, तो आमतौर पर इसे प्रतिलिपि बनाने और साथ ही मुक्त करने की आवश्यकता होती है।

यदि आपके वर्ग प्रबंधन के संसाधन की प्रतिलिपि बनाने के लिए कोई अच्छा अर्थपूर्ण नहीं है, तो घोषणा करके प्रतिलिपि बनाने पर विचार करें (नहीं परिभाषित करने) कॉपी कन्स्ट्रक्टर और असाइनमेंट ऑपरेटर के रूप में private

(ध्यान दें कि सी ++ मानक (जो सी ++ 11 है) का आगामी नया संस्करण सी ++ में ले जाने वाले अर्थशास्त्र को जोड़ता है, जो संभवतः तीन के नियम को बदल देगा। हालांकि, मुझे सी ++ 11 सेक्शन लिखने के लिए बहुत कम पता है तीन के नियम के बारे में।)


451
2017-11-13 14:22



प्रतिलिपि को रोकने का एक और समाधान एक वर्ग से उत्तराधिकारी (निजी तौर पर) है जिसे कॉपी नहीं किया जा सकता है (जैसे boost::noncopyable)। यह भी बहुत स्पष्ट हो सकता है। मुझे लगता है कि सी ++ 0x और "हटाने" कार्यों की संभावना यहां मदद कर सकती है, लेकिन वाक्यविन्यास भूल गई: / - Matthieu M.
@ माथीथू: हाँ, यह भी काम करता है। लेकिन जब तक noncopyable std lib का हिस्सा है, मैं इसे एक सुधार में नहीं मानता। (ओह, और यदि आप विलोपन सिंटैक्स भूल गए हैं, तो आप कभी भी जानते थे कि मोर एथन भूल गए हैं। :)) - sbi
तीन और सी ++ 1x के नियम पर कोई अपडेट? - Daan Timmer
@ दान: देखें यह जवाब। हालांकि, मैं चिपकने की सिफारिश करता हूं Martinhoकी शून्य का नियम। मेरे लिए, यह पिछले दशक में सी ++ के लिए अंगूठे के सबसे महत्वपूर्ण नियमों में से एक है। - sbi
मार्टिन्हो का शून्य का नियम अब स्थित है यहाँ - Diego


बड़े तीनों का कानून ऊपर निर्दिष्ट है।

एक आसान उदाहरण, सादे अंग्रेजी में, जिस तरह की समस्या हल होती है:

गैर डिफ़ॉल्ट विनाशक

आपने अपने कन्स्ट्रक्टर में मेमोरी आवंटित की है और इसलिए इसे हटाने के लिए आपको एक विनाशक लिखना होगा। अन्यथा आप एक स्मृति रिसाव का कारण बनेंगे।

आपको लगता है कि यह काम किया गया है।

समस्या होगी, अगर आपकी प्रतिलिपि एक प्रतिलिपि बनाई गई है, तो प्रतिलिपि मूल वस्तु के समान स्मृति को इंगित करेगी।

एक बार, इनमें से एक स्मृति को अपने विनाशक में हटा देता है, दूसरे में अमान्य स्मृति के लिए एक सूचक होगा (इसे एक खतरनाक सूचक कहा जाता है) जब यह इसका उपयोग करने की कोशिश करता है तो चीजें बालों वाली हो रही हैं।

इसलिए, आप एक कॉपी कन्स्ट्रक्टर लिखते हैं ताकि यह नई वस्तुओं को नष्ट करने के लिए स्मृति के अपने टुकड़े आवंटित कर सके।

असाइनमेंट ऑपरेटर और कॉपी कन्स्ट्रक्टर

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

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

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


134
2018-05-14 14:22



तो अगर हम एक कॉपी कन्स्ट्रक्टर का उपयोग करते हैं तो प्रतिलिपि बनाई जाती है लेकिन एक अलग मेमोरी लोकेशन पर पूरी तरह से होती है और यदि हम कॉपी कन्स्ट्रक्टर का उपयोग नहीं करते हैं तो प्रतिलिपि बनाई जाती है लेकिन यह उसी स्मृति स्थान को इंगित करती है। क्या आप यही कहने की कोशिश कर रहे हैं? तो कॉपी कन्स्ट्रक्टर के बिना एक प्रतिलिपि का मतलब है कि एक नया सूचक वहां होगा, लेकिन उसी स्मृति स्थान को इंगित करता है, हालांकि यदि हमारे पास उपयोगकर्ता द्वारा स्पष्ट रूप से परिभाषित प्रतिलिपि बनाने वाला कन्स्ट्रक्टर है तो हमारे पास एक अलग पॉइंटर होगा जो अलग-अलग स्मृति स्थान पर इंगित करता है लेकिन डेटा है। - Unbreakable
क्षमा करें, मैंने इस उम्र पहले जवाब दिया था, लेकिन मेरा जवाब अभी भी यहां प्रतीत नहीं होता है :-( असल में, हाँ - आपको यह मिलता है :-) - Stefan
सिद्धांत प्रतिलिपि ऑपरेटर ऑपरेटर को कैसे अनुपालन करता है? यह उत्तर अधिक उपयोगी होगा यदि तीसरे नियम में तीसरे का उल्लेख किया जाएगा। - DBedrenko
@ डीबेड्रेन्को, "आप एक कॉपी कन्स्ट्रक्टर लिखते हैं ताकि यह नई वस्तुओं को स्मृति के अपने टुकड़े आवंटित कर सके ..." यह वही सिद्धांत है जो कॉपी असाइनमेंट ऑपरेटर तक फैला हुआ है। क्या आपको नहीं लगता कि मैंने यह स्पष्ट कर दिया है? - Stefan
@Stefan हाँ, बहुत बहुत धन्यवाद! - DBedrenko


असल में यदि आपके पास विनाशक है (डिफ़ॉल्ट विनाशक नहीं) तो इसका मतलब है कि आपके द्वारा परिभाषित कक्षा में कुछ स्मृति आवंटन है। मान लें कि कक्षा का उपयोग कुछ क्लाइंट कोड या आपके द्वारा किया जाता है।

    MyClass x(a, b);
    MyClass y(c, d);
    x = y; // This is a shallow copy if assignment operator is not provided

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


37
2017-12-31 19:29





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

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

class Car //A very simple class just to demonstrate what these definitions mean.
//It's pseudocode C++/Javaish, I assume strings do not need to be allocated.
{
private String sPrintColor;
private String sModel;
private String sMake;

public changePaint(String newColor)
{
   this.sPrintColor = newColor;
}

public Car(String model, String make, String color) //Constructor
{
   this.sPrintColor = color;
   this.sModel = model;
   this.sMake = make;
}

public ~Car() //Destructor
{
//Because we did not create any custom types, we aren't adding more code.
//Anytime your object goes out of scope / program collects garbage / etc. this guy gets called + all other related destructors.
//Since we did not use anything but strings, we have nothing additional to handle.
//The assumption is being made that the 3 strings will be handled by string's destructor and that it is being called automatically--if this were not the case you would need to do it here.
}

public Car(const Car &other) // Copy Constructor
{
   this.sPrintColor = other.sPrintColor;
   this.sModel = other.sModel;
   this.sMake = other.sMake;
}
public Car &operator =(const Car &other) // Assignment Operator
{
   if(this != &other)
   {
      this.sPrintColor = other.sPrintColor;
      this.sModel = other.sModel;
      this.sMake = other.sMake;
   }
   return *this;
}

}

एक गहरी प्रतिलिपि यह है कि यदि हम किसी ऑब्जेक्ट की घोषणा करते हैं और फिर ऑब्जेक्ट की पूरी तरह से अलग प्रतिलिपि बनाते हैं ... हम 2 ऑब्जेक्ट्स के साथ 2 मेमोरी के सेट में समाप्त होते हैं।

Car car1 = new Car("mustang", "ford", "red");
Car car2 = car1; //Call the copy constructor
car2.changePaint("green");
//car2 is now green but car1 is still red.

अब चलो कुछ अजीब करते हैं। आइए मान लें कि कार 2 या तो प्रोग्राम गलत है या जानबूझकर इसका मतलब है कि वास्तविक स्मृति को साझा करने के लिए कार 1 बनाया गया है। (आमतौर पर ऐसा करने में गलती होती है और कक्षाओं में आमतौर पर कंबल होता है जिस पर चर्चा की जाती है।) दिखाएं कि जब भी आप कार 2 के बारे में पूछते हैं, तो आप वास्तव में कार 1 की मेमोरी स्पेस के सूचक को हल कर रहे हैं ... यह एक उथल-पुथल की तुलना में कम या कम है है।

//Shallow copy example
//Assume we're in C++ because it's standard behavior is to shallow copy objects if you do not have a constructor written for an operation.
//Now let's assume I do not have any code for the assignment or copy operations like I do above...with those now gone, C++ will use the default.

 Car car1 = new Car("ford", "mustang", "red"); 
 Car car2 = car1; 
 car2.changePaint("green");//car1 is also now green 
 delete car2;/*I get rid of my car which is also really your car...I told C++ to resolve 
 the address of where car2 exists and delete the memory...which is also
 the memory associated with your car.*/
 car1.changePaint("red");/*program will likely crash because this area is
 no longer allocated to the program.*/

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

कॉपी कन्स्ट्रक्टर और कॉपी असाइनमेंट ऑपरेटर क्या हैं? मैंने पहले से ही उन्हें ऊपर इस्तेमाल किया है। जब आप कोड टाइप करते हैं तो कॉपी कन्स्ट्रक्टर को कॉल किया जाता है Car car2 = car1;  अनिवार्य रूप से यदि आप एक चर घोषित करते हैं और इसे एक पंक्ति में असाइन करते हैं, तो वह तब होता है जब कॉपी कन्स्ट्रक्टर कहा जाता है। असाइनमेंट ऑपरेटर तब होता है जब आप बराबर चिह्न का उपयोग करते हैं--car2 = car1;। नोटिस car2 एक ही बयान में घोषित नहीं किया गया है। इन परिचालनों के लिए आपके द्वारा लिखे गए कोड के दो भाग बहुत समान हैं। असल में विशिष्ट डिजाइन पैटर्न में एक और फ़ंक्शन होता है जिसे आप संतुष्ट करने के बाद सबकुछ सेट करने के लिए कॉल करते हैं प्रारंभिक प्रति / असाइनमेंट वैध है - यदि आप मेरे द्वारा लिखे गए लैंडहैंड कोड को देखते हैं, तो फ़ंक्शन लगभग समान होते हैं।

मुझे उन्हें खुद घोषित करने की आवश्यकता कब होगी? यदि आप ऐसे कोड नहीं लिख रहे हैं जिन्हें साझा किया जाना है या किसी तरह से उत्पादन के लिए, आपको वास्तव में केवल उन्हें घोषित करने की आवश्यकता होती है जब आपको उनकी आवश्यकता होती है। यदि आप इसे 'दुर्घटना से' उपयोग करना चुनते हैं और एक नहीं बनाते हैं, तो आपको यह जानने की आवश्यकता है कि आपकी प्रोग्राम भाषा क्या करती है - यानी। आप संकलक डिफ़ॉल्ट प्राप्त करते हैं। उदाहरण के लिए मैं शायद ही कभी कॉपी कन्स्ट्रक्टर का उपयोग करता हूं, लेकिन असाइनमेंट ऑपरेटर ओवरराइड बहुत आम हैं। क्या आप जानते थे कि आप किस अतिरिक्त, घटाव आदि का मतलब ओवरराइड कर सकते हैं?

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


27
2017-10-17 16:37



सवाल सी ++ टैग किया गया था। यह छद्म-कोड एक्सपोज़िशन अच्छी तरह से परिभाषित "नियम तीन" के बारे में कुछ भी स्पष्ट करने के लिए बहुत कम करता है, और केवल सबसे खराब भ्रम फैलता है। - sehe


मुझे उन्हें खुद घोषित करने की आवश्यकता कब होगी?

तीन नियमों का कहना है कि यदि आप इनमें से कोई भी घोषित करते हैं

  1. प्रतिलिपि निर्माता
  2. असाइनमेंट ऑपरेटर कॉपी करें
  3. नाशक

तो आपको सभी तीन घोषित करना चाहिए। यह अवलोकन से निकला कि प्रतिलिपि ऑपरेशन के अर्थ को लेने की आवश्यकता लगभग हमेशा किसी प्रकार के संसाधन प्रबंधन करने वाले वर्ग से निकलती है, और लगभग हमेशा यह संकेत मिलता है कि

  • जो भी संसाधन प्रबंधन एक कॉपी ऑपरेशन में किया जा रहा था, शायद अन्य कॉपी ऑपरेशन में किया जाना चाहिए और

  • वर्ग विनाशक संसाधन के प्रबंधन में भी भाग लेगा (आमतौर पर इसे जारी करता है)। प्रबंधित करने के लिए क्लासिक संसाधन स्मृति थी, और यही कारण है कि सभी मानक पुस्तकालय वर्गों स्मृति का प्रबंधन करें (उदाहरण के लिए, एसटीएल कंटेनर जो गतिशील स्मृति प्रबंधन करते हैं) सभी "बड़े तीन" घोषित करते हैं: दोनों प्रतिलिपि संचालन और विनाशक।

तीन के नियम का परिणाम यह है कि उपयोगकर्ता द्वारा घोषित विनाशक की उपस्थिति इंगित करती है कि सरल सदस्यवार प्रतिलिपि कक्षा में प्रतिलिपि संचालन के लिए उपयुक्त होने की संभावना नहीं है। बदले में, यह सुझाव देता है कि यदि एक वर्ग एक विनाशक घोषित करता है, तो प्रतिलिपि संचालन शायद स्वचालित रूप से जेनरेट नहीं किया जाना चाहिए, क्योंकि वे सही काम नहीं करेंगे। उस समय सी ++ 98 को अपनाया गया था, तर्क की इस पंक्ति का महत्व पूरी तरह से सराहना नहीं किया गया था, इसलिए सी ++ 98 में, उपयोगकर्ता द्वारा घोषित घोषित करने वाले व्यक्ति का अस्तित्व प्रतिलिपि बनाने के लिए कंपाइलर्स की इच्छा पर कोई प्रभाव नहीं पड़ा। यह सी ++ 11 में मामला जारी है, लेकिन केवल इसलिए कि उन परिस्थितियों को सीमित करना जिनके अंतर्गत प्रतिलिपि संचालन उत्पन्न होते हैं, वे बहुत अधिक विरासत कोड तोड़ देंगे।

मैं अपनी वस्तुओं को कॉपी करने से कैसे रोक सकता हूं?

निजी एक्सेस विनिर्देशक के रूप में कॉपी कन्स्ट्रक्टर और कॉपी असाइनमेंट ऑपरेटर घोषित करें।

class MemoryBlock
{
public:

//code here

private:
MemoryBlock(const MemoryBlock& other)
{
   cout<<"copy constructor"<<endl;
}

// Copy assignment operator.
MemoryBlock& operator=(const MemoryBlock& other)
{
 return *this;
}
};

int main()
{
   MemoryBlock a;
   MemoryBlock b(a);
}

सी ++ 11 में आप कॉपी कन्स्ट्रक्टर और असाइनमेंट ऑपरेटर को भी घोषित कर सकते हैं

class MemoryBlock
{
public:
MemoryBlock(const MemoryBlock& other) = delete

// Copy assignment operator.
MemoryBlock& operator=(const MemoryBlock& other) =delete
};


int main()
{
   MemoryBlock a;
   MemoryBlock b(a);
}

19
2018-01-12 09:54





मौजूदा उत्तरों में से कई पहले ही कॉपी कन्स्ट्रक्टर, असाइनमेंट ऑपरेटर और विनाशक को छूते हैं। हालांकि, पोस्ट सी ++ 11 में, स्थानांतरण अर्थात् परिचय की शुरुआत 3 से अधिक हो सकती है।

हाल ही में माइकल क्लेयसे ने एक बात की जो इस विषय को छूता है: http://channel9.msdn.com/events/CPP/C-PP-Con-2014/The-Canonical-Class


9
2018-01-07 05:38





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

सी ++ में कन्स्ट्रक्टर कॉपी करें एक विशेष निर्माता है। इसका उपयोग एक नई वस्तु बनाने के लिए किया जाता है, जो एक मौजूदा वस्तु की एक प्रति के बराबर नई वस्तु है।

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

त्वरित उदाहरण हैं:

// default constructor
My_Class a;

// copy constructor
My_Class b(a);

// copy constructor
My_Class c = a;

// copy assignment operator
b = a;

5
2017-08-12 04:27



हाय, आपका जवाब कुछ भी नया नहीं जोड़ता है। अन्य विषय इस विषय को और अधिक गहराई से ढंकते हैं, और अधिक सटीक रूप से - आपका उत्तर अनुमानित है और वास्तव में कुछ स्थानों में गलत है (अर्थात् यहां "जरूरी" नहीं है; यह "शायद" चाहिए ")। प्रश्नों के इस तरह के उत्तर पोस्ट करते समय यह वास्तव में आपके लायक नहीं होगा, जिनका पहले से ही उत्तर दिया गया है। जब तक आपके पास जोड़ने के लिए नई चीजें न हों। - Mat
इसके अलावा, वहाँ हैं चार त्वरित उदाहरण, जो हैं किसी न किसी तरह से संबंधित दो का तीन कि तीन का नियम बात कर रहा है। बहुत ज्यादा भ्रम। - anatolyg