- किसी वस्तु की नकल करने का क्या मतलब है?
- क्या हैं प्रतिलिपि निर्माता और प्रति असाइनमेंट ऑपरेटर ?
- मुझे उन्हें खुद कब घोषित करने की आवश्यकता है?
- मैं अपनी वस्तुओं को कॉपी होने से कैसे रोक सकता हूं?
जवाबों:
C ++ उपयोगकर्ता-परिभाषित प्रकारों के चर को शब्दार्थ के साथ मानता है । इसका मतलब यह है कि वस्तुओं को विभिन्न संदर्भों में स्पष्ट रूप से कॉपी किया जाता है, और हमें यह समझना चाहिए कि "वस्तु की नकल" वास्तव में क्या है।
आइए एक सरल उदाहरण पर विचार करें:
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]
डिफ़ॉल्ट रूप से, किसी ऑब्जेक्ट को कॉपी करने का अर्थ है उसके सदस्यों की प्रतिलिपि बनाना:
गैर-संघ श्रेणी X के लिए अनुमानित रूप से परिभाषित कॉपी कंस्ट्रक्टर अपने सब -जेक्ट्स की एक सदस्यवार कॉपी करता है। [n3126.pdf सेक्शन 12.8 316]
गैर-संघ श्रेणी X के लिए अनुमानित रूप से परिभाषित कॉपी असाइनमेंट ऑपरेटर अपने सबोबिज के सदस्यवार कॉपी असाइनमेंट करता है। [n3126.pdf सेक्शन 12.8 330]
इस 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]
तो हमें उन विशेष सदस्य कार्यों को स्पष्ट रूप से कब घोषित करना चाहिए? जब हमारा वर्ग किसी संसाधन का प्रबंधन करता है , अर्थात जब वर्ग का कोई वस्तु उस संसाधन के लिए जिम्मेदार होता है। यही कारण है कि आम तौर पर इसका मतलब संसाधन है हासिल कर ली निर्माता में (या पारित कर दिया निर्माता में) और जारी किया नाशक में।
हमें पूर्व-मानक C ++ के समय पर वापस जाना चाहिए। इस तरह की कोई बात नहीं थी 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
सदस्य की नकल करना एक पॉइंटर को कॉपी करता है, न कि कैरेक्टर एरे की ओर इशारा करता है! इसके कई अप्रिय प्रभाव हैं:
a
देखा जा सकता है b
।b
नष्ट हो जाने के बाद , a.name
झूलने वाला सूचक है।a
नष्ट हो जाता है, तो झूलने वाले सूचक को हटाने से अपरिभाषित व्यवहार होता है ।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
इन्हें हटा सकते हैं या इन्हें हटाए जाने की घोषणा कर सकते हैं (C ++ 11 और इसके बाद के संस्करण में):
person(const person& that) = delete;
person& operator=(const person& that) = delete;
कभी-कभी आपको एक संसाधन को प्रबंधित करने वाले वर्ग को लागू करने की आवश्यकता होती है। (कभी भी एक ही वर्ग में कई संसाधनों का प्रबंधन न करें, इससे केवल पीड़ा होगी।) उस स्थिति में, तीन का नियम याद रखें :
यदि आपको स्पष्ट रूप से विध्वंसक, कॉपी कंस्ट्रक्टर या कॉपी असाइनमेंट ऑपरेटर की स्पष्ट रूप से घोषणा करने की आवश्यकता है, तो आपको संभवतः उन तीनों को स्पष्ट रूप से घोषित करने की आवश्यकता है।
(दुर्भाग्य से, यह "नियम" C ++ मानक या मेरे द्वारा ज्ञात किसी भी संकलक द्वारा लागू नहीं किया गया है।)
C ++ 11 से, एक ऑब्जेक्ट में 2 अतिरिक्त विशेष सदस्य कार्य हैं: मूव कंस्ट्रक्टर और मूव असाइनमेंट। पांच राज्यों का नियम इन कार्यों को लागू करने के लिए भी है।
हस्ताक्षर के साथ एक उदाहरण:
class person
{
std::string name;
int age;
public:
person(const std::string& name, int age); // Ctor
person(const person &) = default; // Copy Ctor
person(person &&) noexcept = default; // Move Ctor
person& operator=(const person &) = default; // Copy Assignment
person& operator=(person &&) noexcept = default; // Move Assignment
~person() noexcept = default; // Dtor
};
3/5 के नियम को 0/3/5 के नियम के रूप में भी जाना जाता है। नियम का शून्य भाग बताता है कि आपको अपनी कक्षा बनाते समय विशेष सदस्य कार्यों में से किसी को भी लिखने की अनुमति नहीं है।
ज्यादातर समय, आपको स्वयं एक संसाधन का प्रबंधन करने की आवश्यकता नहीं है, क्योंकि एक मौजूदा वर्ग जैसे कि std::string
यह आपके लिए पहले से ही है। किसी std::string
सदस्य का उपयोग करके बस सरल कोड की तुलना करें और एक का उपयोग करके त्रुटि-प्रवण विकल्प का उपयोग करें char*
और आपको आश्वस्त होना चाहिए। जब तक आप कच्चे पॉइंटर सदस्यों से दूर रहते हैं, तब तक तीन का नियम आपके अपने कोड की चिंता करने की संभावना नहीं है।
तीन के नियम सी ++ के लिए अंगूठे का एक नियम है, मूल रूप से कह रही है
यदि आपकी कक्षा को किसी की आवश्यकता है
- एक प्रतिलिपि निर्माता ,
- एक असाइनमेंट ऑपरेटर ,
- या एक विध्वंसक ,
स्पष्ट रूप से परिभाषित किया गया है, तो इसके तीनों की आवश्यकता है ।
इसका कारण यह है कि उनमें से तीनों का उपयोग आमतौर पर एक संसाधन का प्रबंधन करने के लिए किया जाता है, और यदि आपकी कक्षा एक संसाधन का प्रबंधन करती है, तो इसे आमतौर पर नकल के साथ-साथ मुक्त प्रबंधन की आवश्यकता होती है।
यदि आपके वर्ग के संसाधन को कॉपी करने के लिए कोई अच्छा शब्दार्थ नहीं है, तो कॉपी कंस्ट्रक्टर और असाइनमेंट ऑपरेटर के रूप में घोषित ( परिभाषित नहीं ) करके कॉपी करने से मना करें private
।
(ध्यान दें कि C ++ मानक का आगामी नया संस्करण (जो कि C ++ 11 है), C अर्थ को स्थानांतरित करता है C ++, जो संभवतः तीन के नियम को बदल देगा। थ्री के नियम के बारे में।)
boost::noncopyable
) नहीं किया जा सकता है । यह ज्यादा साफ भी हो सकता है। मुझे लगता है कि C ++ 0x और "डिलीट" कार्यों की संभावना यहां मदद कर सकती है, लेकिन वाक्यविन्यास भूल गए: /
noncopyable
एसटीडी लिब का हिस्सा नहीं होता, मैं इसे ज्यादा सुधार नहीं मानता। (ओह, और अगर आप डिलीट सिंटैक्स भूल गए, तो आप मोर एथन को भूल गए जो मैं कभी भी जानता था। :)
)
बड़े तीन का कानून ऊपर निर्दिष्ट है।
एक आसान उदाहरण, सादे अंग्रेजी में, इस तरह की समस्या हल करती है:
गैर डिफ़ॉल्ट विध्वंसक
आपने अपने निर्माता में मेमोरी आवंटित की है और इसलिए इसे हटाने के लिए आपको एक विध्वंसक लिखना होगा। अन्यथा आप स्मृति रिसाव का कारण बनेंगे।
आप सोच सकते हैं कि यह काम किया गया है।
समस्या यह होगी, यदि कोई प्रतिलिपि आपकी ऑब्जेक्ट से बनी है, तो कॉपी मूल ऑब्जेक्ट के समान मेमोरी को इंगित करेगी।
एक बार, इनमें से एक मेमोरी को उसके विध्वंसक में हटा देता है, दूसरे के पास अमान्य मेमोरी के लिए एक पॉइंटर होगा (इसे डैंगलिंग पॉइंटर कहा जाता है) जब वह इसका उपयोग करने की कोशिश करता है तो चीजें बालों वाली हो जाती हैं।
इसलिए, आप एक कॉपी कंस्ट्रक्टर लिखते हैं ताकि यह नई वस्तुओं को नष्ट करने के लिए स्मृति के अपने टुकड़ों को आवंटित करे।
असाइनमेंट ऑपरेटर और कॉपी कंस्ट्रक्टर
आपने अपने निर्माता को अपने वर्ग के सदस्य सूचक को मेमोरी आवंटित की। जब आप इस वर्ग की एक वस्तु को कॉपी करते हैं तो डिफ़ॉल्ट असाइनमेंट ऑपरेटर और कॉपी कंस्ट्रक्टर इस सदस्य पॉइंटर के मूल्य को नई वस्तु में कॉपी कर देगा।
इसका मतलब यह है कि नई वस्तु और पुरानी वस्तु एक ही मेमोरी के टुकड़े की ओर इशारा करती है, इसलिए जब आप इसे एक ऑब्जेक्ट में बदलते हैं तो इसे दूसरे ओब्जेक्ट के लिए भी बदल दिया जाएगा। यदि एक ऑब्जेक्ट इस मेमोरी को हटाता है, तो दूसरा इसे उपयोग करने की कोशिश करेगा - eek।
इसे हल करने के लिए आप कॉपी कंस्ट्रक्टर और असाइनमेंट ऑपरेटर का अपना संस्करण लिखें। आपके संस्करण नई वस्तुओं को अलग-अलग मेमोरी आवंटित करते हैं और उन मानों की प्रतिलिपि बनाते हैं जो पहला सूचक इसके पते के बजाय इंगित कर रहा है।
मूल रूप से यदि आपके पास एक विध्वंसक है (डिफ़ॉल्ट डिस्ट्रक्टर नहीं है) तो इसका मतलब है कि जिस वर्ग को आपने परिभाषित किया है, उसमें कुछ मेमोरी आवंटन है। मान लीजिए कि क्लास का उपयोग कुछ क्लाइंट कोड या आपके द्वारा किया जाता है।
MyClass x(a, b);
MyClass y(c, d);
x = y; // This is a shallow copy if assignment operator is not provided
यदि MyClass में केवल कुछ आदिम टाइप किए गए सदस्य हैं, तो एक डिफ़ॉल्ट असाइनमेंट ऑपरेटर काम करेगा, लेकिन अगर इसमें कुछ पॉइंटर सदस्य और ऑब्जेक्ट हैं, जिनके पास असाइनमेंट ऑपरेटर नहीं हैं, तो परिणाम अप्रत्याशित होगा। इसलिए हम कह सकते हैं कि यदि किसी वर्ग के विध्वंसक को हटाने के लिए कुछ है, तो हमें एक गहरी कॉपी ऑपरेटर की आवश्यकता हो सकती है, जिसका अर्थ है कि हमें एक कॉपी कंस्ट्रक्टर और असाइनमेंट ऑपरेटर प्रदान करना चाहिए।
किसी वस्तु की नकल करने का क्या मतलब है? कुछ तरीके हैं जिनसे आप वस्तुओं को कॉपी कर सकते हैं - चलो उन 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
एक ही बयान में घोषित नहीं किया गया है। इन कार्यों के लिए आपके द्वारा लिखे गए कोड के दो भाग बहुत समान हैं। वास्तव में विशिष्ट डिज़ाइन पैटर्न में एक और फ़ंक्शन होता है जिसे आप एक बार सब कुछ सेट करने के लिए कहते हैं, जब आप संतुष्ट हो जाते हैं कि प्रारंभिक कॉपी / असाइनमेंट वैध है - यदि आप मेरे द्वारा लिखे गए लॉन्गैंड कोड को देखते हैं, तो फ़ंक्शन लगभग समान हैं।
मुझे उन्हें खुद कब घोषित करने की आवश्यकता है? यदि आप ऐसा कोड नहीं लिख रहे हैं जिसे किसी तरीके से साझा किया जाना है या उत्पादन के लिए है, तो आपको वास्तव में केवल उनकी आवश्यकता होने पर उन्हें घोषित करने की आवश्यकता है। आपको यह जानने की आवश्यकता है कि यदि आप इसे 'दुर्घटना से' इस्तेमाल करने के लिए चुनते हैं तो आपकी प्रोग्राम भाषा क्या करती है और आपने इसे नहीं बनाया है - यानी आपको कंपाइलर डिफ़ॉल्ट मिलता है। मैं शायद ही कभी उदाहरण के लिए कॉपी कंस्ट्रक्टर का उपयोग करता हूं, लेकिन असाइनमेंट ऑपरेटर ओवरराइड बहुत आम हैं। क्या आप जानते हैं कि आप इसके अलावा, घटाव, इत्यादि को भी ओवरराइड कर सकते हैं?
मैं अपनी वस्तुओं को कॉपी होने से कैसे रोक सकता हूं? निजी फ़ंक्शन के साथ अपनी ऑब्जेक्ट के लिए मेमोरी आवंटित करने के लिए अनुमति देने के सभी तरीकों को ओवरराइड करना एक उचित शुरुआत है। यदि आप वास्तव में लोगों को उनकी नकल नहीं करना चाहते हैं, तो आप इसे सार्वजनिक कर सकते हैं और प्रोग्रामर को एक अपवाद फेंककर और ऑब्जेक्ट को कॉपी नहीं करके सतर्क कर सकते हैं।
मुझे उन्हें खुद कब घोषित करने की आवश्यकता है?
तीन नियमों में कहा गया है कि यदि आप किसी भी की घोषणा करते हैं
तो आपको तीनों की घोषणा करनी चाहिए। यह अवलोकन से बाहर हुआ कि कॉपी ऑपरेशन के अर्थ को लेने की आवश्यकता लगभग हमेशा किसी न किसी तरह के संसाधन प्रबंधन को करने वाले वर्ग से होती है, और यह लगभग हमेशा निहित होता है
एक कॉपी ऑपरेशन में जो भी संसाधन प्रबंधन किया जा रहा था, वह शायद दूसरे कॉपी ऑपरेशन और में किया जाना चाहिए
वर्ग विध्वंसक भी संसाधन के प्रबंधन (आमतौर पर इसे जारी करने) में भाग ले रहा होगा। प्रबंधित किया जाने वाला क्लासिक संसाधन मेमोरी था, और यही कारण है कि सभी मानक लाइब्रेरी कक्षाएं जो मेमोरी का प्रबंधन करती हैं (जैसे, एसटीएल कंटेनर जो गतिशील मेमोरी प्रबंधन करते हैं) सभी "बड़े तीन" की घोषणा करते हैं: दोनों कॉपी ऑपरेशन और एक विध्वंसक।
तीन के नियम का एक परिणाम यह है कि एक उपयोगकर्ता-घोषित विध्वंसक की उपस्थिति इंगित करती है कि कक्षा में प्रतिलिपि संचालन के लिए सरल सदस्य वार प्रति उपयुक्त नहीं है। बदले में, यह सुझाव देता है कि यदि कोई वर्ग एक विध्वंसक घोषित करता है, तो कॉपी संचालन शायद स्वचालित रूप से उत्पन्न नहीं होना चाहिए, क्योंकि वे सही काम नहीं करेंगे। जिस समय C ++ 98 को अपनाया गया था, उस तर्क की इस पंक्ति के महत्व को पूरी तरह से सराहा नहीं गया था, इसलिए C ++ 98 में, एक उपयोगकर्ता द्वारा घोषित विध्वंसक के अस्तित्व को कॉपी ऑपरेशन उत्पन्न करने के लिए संकलक की इच्छा पर कोई प्रभाव नहीं पड़ा। C ++ 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);
}
C ++ 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);
}
मौजूदा उत्तरों में से कई पहले से ही कॉपी कंस्ट्रक्टर, असाइनमेंट ऑपरेटर और डिस्ट्रक्टर को छूते हैं। हालाँकि, C ++ 11 में, पोस्ट सिमेंटिक की शुरूआत 3 से परे इसका विस्तार कर सकती है।
हाल ही में माइकल क्लैसे ने एक बात की जो इस विषय को छूती है: http://channel9.msdn.com/events/CPP/C-PP-Con-2014/The-Canonical-Class
C ++ में तीन का नियम डिजाइन का एक मूलभूत सिद्धांत और तीन आवश्यकताओं का विकास है यदि निम्न सदस्य फ़ंक्शन में से एक में स्पष्ट परिभाषा है, तो प्रोग्रामर को अन्य दो सदस्यों के कार्यों को एक साथ परिभाषित करना चाहिए। निम्नलिखित तीन सदस्य कार्य अपरिहार्य हैं: विध्वंसक, कॉपी कंस्ट्रक्टर, कॉपी असाइनमेंट ऑपरेटर।
C ++ में कॉपी कंस्ट्रक्टर एक विशेष कंस्ट्रक्टर है। इसका उपयोग एक नई वस्तु के निर्माण के लिए किया जाता है, जो कि किसी मौजूदा वस्तु की नकल के बराबर नई वस्तु है।
कॉपी असाइनमेंट ऑपरेटर एक विशेष असाइनमेंट ऑपरेटर है जो आमतौर पर किसी मौजूदा ऑब्जेक्ट को उसी प्रकार के ऑब्जेक्ट के अन्य को निर्दिष्ट करने के लिए उपयोग किया जाता है।
इसके त्वरित उदाहरण हैं:
// default constructor
My_Class a;
// copy constructor
My_Class b(a);
// copy constructor
My_Class c = a;
// copy assignment operator
b = a;
c++-faq
।