मुझे पता है कि C ++ कंपाइलर एक क्लास के लिए कॉपी कंस्ट्रक्टर बनाता है। हमें किस मामले में उपयोगकर्ता द्वारा परिभाषित कॉपी कंस्ट्रक्टर लिखना होगा? क्या आप कुछ उदाहरण दे सकते हैं?
मुझे पता है कि C ++ कंपाइलर एक क्लास के लिए कॉपी कंस्ट्रक्टर बनाता है। हमें किस मामले में उपयोगकर्ता द्वारा परिभाषित कॉपी कंस्ट्रक्टर लिखना होगा? क्या आप कुछ उदाहरण दे सकते हैं?
जवाबों:
कंपाइलर द्वारा बनाई गई कॉपी कंस्ट्रक्टर सदस्य वार कॉपीिंग करता है। कभी-कभी यह पर्याप्त नहीं होता है। उदाहरण के लिए:
class Class {
public:
Class( const char* str );
~Class();
private:
char* stored;
};
Class::Class( const char* str )
{
stored = new char[srtlen( str ) + 1 ];
strcpy( stored, str );
}
Class::~Class()
{
delete[] stored;
}
इस मामले में सदस्य-वार कॉपी कॉपी stored
करने वाले बफर की नकल नहीं करेंगे (केवल पॉइंटर को कॉपी किया जाएगा), इसलिए बफर को साझा करने वाली कॉपी को नष्ट करने वाली पहली कॉल delete[]
सफलतापूर्वक होगी और दूसरी अपरिभाषित व्यवहार में चलेगी। आपको गहरी कॉपी कॉपी निर्माता (और असाइनमेंट ऑपरेटर के रूप में अच्छी तरह से) की आवश्यकता है।
Class::Class( const Class& another )
{
stored = new char[strlen(another.stored) + 1];
strcpy( stored, another.stored );
}
void Class::operator = ( const Class& another )
{
char* temp = new char[strlen(another.stored) + 1];
strcpy( temp, another.stored);
delete[] stored;
stored = temp;
}
delete stored[];
और मेरा मानना है कि यह होना चाहिएdelete [] stored;
std::string
। सामान्य विचार यह है कि केवल उपयोगिता वर्ग जो संसाधनों का प्रबंधन करते हैं उन्हें बिग थ्री को अधिभारित करने की आवश्यकता होती है, और अन्य सभी वर्गों को केवल उन उपयोगिता वर्गों का उपयोग करना चाहिए, जिससे बिग थ्री में से किसी को परिभाषित करने की आवश्यकता न हो।
मैं थोड़ा हैरान हूं कि नियम का Rule of Five
हवाला नहीं दिया गया।
यह नियम बहुत सरल है:
पांच का नियम :
जब भी आप डिस्ट्रक्टर, कॉपी कंस्ट्रक्टर, कॉपी असाइनमेंट ऑपरेटर, मूव कंस्ट्रक्टर या मूव असाइनमेंट ऑपरेटर में से किसी एक को लिख रहे होते हैं, तो आपको संभवतः अन्य चार लिखने की आवश्यकता होती है।
लेकिन एक और सामान्य दिशानिर्देश है जिसका आपको पालन करना चाहिए, जो अपवाद-सुरक्षित कोड लिखने की आवश्यकता से उत्पन्न होता है:
प्रत्येक संसाधन को एक समर्पित वस्तु द्वारा प्रबंधित किया जाना चाहिए
यहाँ @sharptooth
कोड अभी भी (ज्यादातर) ठीक है, हालांकि अगर वह अपनी कक्षा में दूसरी विशेषता जोड़ते हैं तो यह नहीं होगा। निम्नलिखित वर्ग पर विचार करें:
class Erroneous
{
public:
Erroneous();
// ... others
private:
Foo* mFoo;
Bar* mBar;
};
Erroneous::Erroneous(): mFoo(new Foo()), mBar(new Bar()) {}
अगर new Bar
फेंकता है तो क्या होता है? आप द्वारा बताई गई वस्तु को कैसे हटाते हैं mFoo
? वहाँ समाधान कर रहे हैं (कार्य स्तर की कोशिश / पकड़ ...), वे सिर्फ पैमाने नहीं है।
स्थिति से निपटने का उचित तरीका कच्चे पॉइंटर्स के बजाय उचित वर्गों का उपयोग करना है।
class Righteous
{
public:
private:
std::unique_ptr<Foo> mFoo;
std::unique_ptr<Bar> mBar;
};
एक ही निर्माता के कार्यान्वयन के साथ (या वास्तव में, उपयोग करते हुए make_unique
), मेरे पास अब मुफ्त में अपवाद सुरक्षा है !!! क्या यह रोमांचक नहीं है? और सबसे अच्छा, मुझे अब उचित विध्वंसक के बारे में चिंता करने की आवश्यकता नहीं है! मुझे अपना Copy Constructor
और Assignment Operator
हालांकि लिखने की ज़रूरत है , क्योंकि unique_ptr
इन ऑपरेशनों को परिभाषित नहीं करता है ... लेकिन यहां कोई फर्क नहीं पड़ता;)
और इसलिए, sharptooth
वर्ग का पुनरीक्षण किया गया:
class Class
{
public:
Class(char const* str): mData(str) {}
private:
std::string mData;
};
मैं आपके बारे में नहीं जानता, लेकिन मुझे मेरा काम आसान लगता है;)
मैं अपने अभ्यास से याद कर सकता हूं और निम्नलिखित मामलों के बारे में सोच सकता हूं जब किसी को कॉपी कंस्ट्रक्टर को स्पष्ट रूप से घोषित / परिभाषित करने से निपटना होगा। मैंने मामलों को दो श्रेणियों में बांटा है
मैं इस भाग में उन मामलों को रखता हूँ जहाँ कॉपी कंस्ट्रक्टर को घोषित / परिभाषित करना, उस प्रकार का उपयोग करके कार्यक्रमों के सही संचालन के लिए आवश्यक है।
इस खंड के माध्यम से पढ़ने के बाद, आप कंपाइलर को अपने आप कॉपी कॉपी बनाने की अनुमति देने के कई नुकसानों के बारे में जानेंगे। इसलिए, के रूप में seand उसकी में बताया गया है इस सवाल का जवाब है, यह हमेशा सुरक्षित एक नया वर्ग के लिए copyability बंद कर देते हैं और करने के लिए है जानबूझ कर बाद में जब वास्तव में जरूरत है इसे सक्षम करें।
एक निजी कॉपी-कंस्ट्रक्टर की घोषणा करें और इसके लिए कार्यान्वयन प्रदान न करें (ताकि बिल्ड लिंकिंग स्टेज पर विफल हो जाए, भले ही उस प्रकार की वस्तुओं को क्लास के अपने दायरे में या उसके दोस्तों द्वारा कॉपी किया गया हो)।
कॉपी-कंस्ट्रक्टर =delete
को अंत में घोषित करें ।
यह सबसे अच्छा समझा जाने वाला मामला है और वास्तव में अन्य उत्तरों में वर्णित एकमात्र है। shaprtooth ने इसे बहुत अच्छी तरह से कवर किया है। मैं केवल उस गहराई से कॉपी करने वाले संसाधनों को जोड़ना चाहता हूं जो विशेष रूप से ऑब्जेक्ट के स्वामित्व में होना चाहिए, किसी भी प्रकार के संसाधनों पर लागू हो सकता है, जिनमें से गतिशील रूप से आवंटित स्मृति केवल एक प्रकार है। जरूरत पड़ने पर किसी वस्तु की गहराई से नकल करने की भी आवश्यकता हो सकती है
एक वर्ग पर विचार करें जहां सभी वस्तुओं - चाहे वे कैसे भी बनाए गए हों - किसी भी तरह पंजीकृत होना चाहिए। कुछ उदाहरण:
सबसे सरल उदाहरण: वर्तमान में मौजूद वस्तुओं की कुल संख्या को बनाए रखना। ऑब्जेक्ट पंजीकरण केवल स्थैतिक काउंटर को बढ़ाने के बारे में है।
एक अधिक जटिल उदाहरण एक सिंगलटन रजिस्ट्री है, जहां उस प्रकार की सभी मौजूदा वस्तुओं के संदर्भ संग्रहीत किए जाते हैं (ताकि उन सभी को सूचनाएं पहुंचाई जा सकें)।
गिने हुए स्मार्ट-पॉइंटर्स को इस श्रेणी में केवल एक विशेष मामला माना जा सकता है: नया पॉइंटर वैश्विक रजिस्ट्री के बजाय साझा संसाधन के साथ "रजिस्टर" करता है।
इस तरह के स्व-पंजीकरण ऑपरेशन को किसी भी प्रकार के निर्माता द्वारा किया जाना चाहिए और कॉपी कंस्ट्रक्टर अपवाद नहीं है।
कुछ वस्तुओं में गैर-तुच्छ आंतरिक संरचना हो सकती है, जो उनके अलग-अलग उप-वस्तुओं के बीच प्रत्यक्ष क्रॉस-रेफरेंस के साथ होती है (वास्तव में, इस मामले को ट्रिगर करने के लिए बस एक ऐसा आंतरिक क्रॉस-रेफरेंस पर्याप्त है)। संकलक-प्रदान की प्रतिलिपि निर्माता आंतरिक टूट जाएगा इंट्रा-वस्तु संघों, उन्हें परिवर्तित अंतर-वस्तु संघों।
एक उदाहरण:
struct MarriedMan;
struct MarriedWoman;
struct MarriedMan {
// ...
MarriedWoman* wife; // association
};
struct MarriedWoman {
// ...
MarriedMan* husband; // association
};
struct MarriedCouple {
MarriedWoman wife; // aggregation
MarriedMan husband; // aggregation
MarriedCouple() {
wife.husband = &husband;
husband.wife = &wife;
}
};
MarriedCouple couple1; // couple1.wife and couple1.husband are spouses
MarriedCouple couple2(couple1);
// Are couple2.wife and couple2.husband indeed spouses?
// Why does couple2.wife say that she is married to couple1.husband?
// Why does couple2.husband say that he is married to couple1.wife?
वहां श्रेणियां जबकि कुछ राज्य में (उदाहरण के लिए डिफ़ॉल्ट-निर्माण-राज्य) और जहां वस्तुओं कॉपी करने के लिए सुरक्षित हैं हो सकता है नहीं सुरक्षित अन्यथा कॉपी करने के लिए। यदि हम सुरक्षित-से-कॉपी ऑब्जेक्ट को कॉपी करने की अनुमति देना चाहते हैं, तो - यदि रक्षात्मक रूप से प्रोग्रामिंग की जाती है - हमें उपयोगकर्ता द्वारा परिभाषित कॉपी कंस्ट्रक्टर में रन-टाइम चेक की आवश्यकता है।
कभी-कभी, एक वर्ग जो प्रतिलिपि योग्य होना चाहिए, वह गैर-प्रतिलिपि योग्य उप-ऑब्जेक्ट को एकत्र करता है। आमतौर पर, यह गैर-पर्यवेक्षित राज्य वाली वस्तुओं के लिए होता है (उस मामले को "अनुकूलन" अनुभाग में और अधिक विस्तार से चर्चा की गई है)। संकलक केवल उस मामले को पहचानने में मदद करता है।
एक वर्ग, जो प्रतिलिपि योग्य होना चाहिए, एक अर्ध-प्रतिलिपि योग्य प्रकार के उप-ऑब्जेक्ट को एकत्रित कर सकता है। एक अर्ध-प्रतिलिपि योग्य प्रकार सख्त अर्थ में एक प्रतिलिपि निर्माता प्रदान नहीं करता है, लेकिन एक अन्य निर्माता है जो ऑब्जेक्ट की एक वैचारिक प्रतिलिपि बनाने की अनुमति देता है। एक प्रकार के अर्ध-प्रतिलिपि बनाने का कारण यह है कि जब प्रकार के प्रतिलिपि शब्दार्थ के बारे में कोई पूर्ण सहमति नहीं है।
उदाहरण के लिए, ऑब्जेक्ट सेल्फ-रजिस्ट्रेशन मामले पर दोबारा गौर करते हुए, हम यह तर्क दे सकते हैं कि ऐसी परिस्थितियाँ हो सकती हैं, जहाँ किसी ऑब्जेक्ट को वैश्विक ऑब्जेक्ट मैनेजर के साथ पंजीकृत होना चाहिए, अगर यह एक पूर्ण स्टैंडअलोन ऑब्जेक्ट है। यदि यह किसी अन्य वस्तु का उप-उद्देश्य है, तो इसे प्रबंधित करने की जिम्मेदारी इसकी युक्त वस्तु के साथ है।
या, उथले और गहरी नकल दोनों का समर्थन किया जाना चाहिए (उनमें से कोई भी डिफ़ॉल्ट नहीं है)।
फिर अंतिम निर्णय उस प्रकार के उपयोगकर्ताओं के लिए छोड़ दिया जाता है - जब वस्तुओं की प्रतिलिपि बनाते हैं, तो उन्हें प्रतिलिपि के इच्छित तरीके को स्पष्ट रूप से निर्दिष्ट करना होगा (अतिरिक्त तर्कों के माध्यम से)।
प्रोग्रामिंग के लिए एक गैर-रक्षात्मक दृष्टिकोण के मामले में, यह भी संभव है कि एक नियमित प्रतिलिपि-निर्माता और अर्ध-प्रतिलिपि-निर्माता दोनों मौजूद हों। यह उचित हो सकता है जब अधिकांश मामलों में एक ही नकल पद्धति लागू की जानी चाहिए, जबकि दुर्लभ लेकिन अच्छी तरह से समझी जाने वाली स्थितियों में वैकल्पिक नकल विधियों का उपयोग किया जाना चाहिए। तब कंपाइलर शिकायत नहीं करेगा कि यह कॉपी कंस्ट्रक्टर को स्पष्ट रूप से परिभाषित करने में असमर्थ है; यह याद रखना और जाँचना उपयोगकर्ताओं की पूरी ज़िम्मेदारी होगी कि उस प्रकार के उप-ऑब्जेक्ट को अर्ध-प्रतिलिपि-निर्माता के माध्यम से कॉपी किया जाए या नहीं।
दुर्लभ मामलों में ऑब्जेक्ट की अवलोकनीय स्थिति का एक सबसेट वस्तु की पहचान का एक अविभाज्य हिस्सा बन सकता है (या माना जाता है) अन्य वस्तुओं के लिए हस्तांतरणीय नहीं होना चाहिए (हालांकि यह कुछ विवादास्पद हो सकता है)।
उदाहरण:
ऑब्जेक्ट का यूआईडी (लेकिन यह भी ऊपर से "सेल्फ-रजिस्ट्रेशन" केस से संबंधित है, क्योंकि आईडी को सेल्फ-रजिस्ट्रेशन के एक्ट में प्राप्त होना चाहिए)।
वस्तु का इतिहास (जैसे कि पूर्ववत करें / फिर से करें) मामले में जब नई वस्तु को स्रोत वस्तु का इतिहास विरासत में नहीं मिलना चाहिए, लेकिन इसके बजाय एक एकल इतिहास आइटम " <TIME> पर <OTHER_OBJECTYID> से कॉपी किया गया " से शुरू करें।
ऐसे मामलों में कॉपी कंस्ट्रक्टर को संबंधित उप-ऑब्जेक्ट को कॉपी करना छोड़ देना चाहिए।
कंपाइलर-प्रदत्त कॉपी कंस्ट्रक्टर का हस्ताक्षर इस बात पर निर्भर करता है कि सब-ऑब्जेक्ट्स के लिए कौन से कॉपी कंस्ट्रक्टर उपलब्ध हैं। यदि कम से कम एक उप-ऑब्जेक्ट में वास्तविक कॉपी कंस्ट्रक्टर नहीं है (स्रोत ऑब्जेक्ट को लगातार संदर्भ द्वारा ले रहा है), लेकिन इसके बजाय एक म्यूटिंग कॉपी-कंस्ट्रक्टर (गैर-निरंतर संदर्भ द्वारा स्रोत ऑब्जेक्ट ले रहा है) तो कंपाइलर के पास कोई विकल्प नहीं होगा लेकिन स्पष्ट रूप से घोषित करने और फिर एक म्यूटिंग कॉपी-कंस्ट्रक्टर को परिभाषित करने के लिए।
अब, क्या होगा अगर उप-ऑब्जेक्ट के प्रकार के "म्यूटिंग" कॉपी-निर्माता वास्तव में स्रोत ऑब्जेक्ट को म्यूट नहीं करता है (और बस एक प्रोग्रामर द्वारा लिखा गया था जो const
कीवर्ड के बारे में नहीं जानता है )? यदि हमारे पास उस कोड को लापता जोड़कर तय नहीं किया जा सकता है const
, तो दूसरा विकल्प हमारे अपने उपयोगकर्ता-परिभाषित कॉपी कंस्ट्रक्टर को एक सही हस्ताक्षर के साथ घोषित करने और पाप को मोड़ने का पाप करना है const_cast
।
एक गाय का कंटेनर जिसने अपने आंतरिक डेटा को प्रत्यक्ष संदर्भ दिया है, उसे निर्माण के समय गहराई से कॉपी किया जाना चाहिए, अन्यथा यह एक संदर्भ गिनती संभाल के रूप में व्यवहार कर सकता है।
हालांकि, गाय एक अनुकूलन तकनीक है, कॉपी कन्स्ट्रक्टर में यह तर्क इसके सही कार्यान्वयन के लिए महत्वपूर्ण है। यही कारण है कि मैंने इस मामले को "अनुकूलन" अनुभाग के बजाय यहां रखा, जहां हम अगले जाते हैं।
निम्नलिखित मामलों में आप अपनी खुद की कॉपी कंस्ट्रक्टर को ऑप्टिमाइज़ेशन चिंताओं से बाहर निकाल सकते हैं।
एक कंटेनर पर विचार करें जो तत्व हटाने के संचालन का समर्थन करता है, लेकिन हटाए गए तत्व को बस चिह्नित करके ऐसा कर सकता है, और बाद में इसके स्लॉट को रीसायकल कर सकता है। जब इस तरह के कंटेनर की एक प्रतिलिपि बनाई जाती है, तो यह "हटाए गए" स्लॉट को संरक्षित करने के बजाय जीवित डेटा को कॉम्पैक्ट करने के लिए समझ में आता है।
किसी ऑब्जेक्ट में डेटा हो सकता है जो उसकी अवलोकन अवस्था का हिस्सा नहीं है। आमतौर पर, यह ऑब्जेक्ट के जीवनकाल के दौरान संचित डेटा को कुछ धीमी गति से संचालित क्वेरी संचालन को गति देने के लिए संचित / याद किया जाता है। उस डेटा को कॉपी करना छोड़ देना सुरक्षित है, क्योंकि यह प्रासंगिक संचालन किए जाने पर (और यदि!) पुनर्गणना हो जाएगा। इस डेटा को कॉपी करना अनुचित हो सकता है, क्योंकि यह जल्दी से अमान्य हो सकता है यदि ऑब्जेक्ट के अवलोकन योग्य स्थिति (जिसमें से कैश्ड डेटा प्राप्त होता है) को म्यूटेशन परिचालनों द्वारा संशोधित किया जाता है (और यदि हम ऑब्जेक्ट को संशोधित नहीं करने जा रहे हैं, तो हम एक गहरी स्थिति क्यों बना रहे हैं? तब कॉपी?)
यह अनुकूलन केवल तभी उचित है जब अवलोकन योग्य स्थिति का प्रतिनिधित्व करने वाले डेटा की तुलना में सहायक डेटा बड़ा हो।
C ++ कॉपी कंस्ट्रक्टर घोषित करके अंतर्निहित प्रतिलिपि को अक्षम करने की अनुमति देता है explicit
। तब उस वर्ग की वस्तुओं को कार्यों में पारित नहीं किया जा सकता है और / या मान से कार्यों से लौटा दिया जाता है। इस ट्रिक का उपयोग एक ऐसे प्रकार के लिए किया जा सकता है जो हल्का प्रतीत होता है लेकिन वास्तव में कॉपी करना बहुत महंगा है (हालांकि, इसे क्वैसी-कॉपी करने योग्य बेहतर विकल्प हो सकता है)।
C ++ 03 में एक कॉपी कंस्ट्रक्टर की घोषणा करते हुए इसे परिभाषित करने की आवश्यकता है (निश्चित रूप से, यदि आप इसका उपयोग करना चाहते हैं)। इसलिए, इस तरह के एक कॉपी कंस्ट्रक्टर के लिए जा रही चिंता से बाहर केवल चर्चा का मतलब है कि आपको एक ही कोड लिखना होगा जो कंपाइलर आपके लिए स्वचालित रूप से उत्पन्न करेगा।
C ++ 11 और नए मानक विशेष सदस्य फ़ंक्शंस (डिफ़ॉल्ट और कॉपी कंस्ट्रक्टर, कॉपी-असाइनमेंट ऑपरेटर, और डिस्ट्रक्टर) को स्पष्ट कार्यान्वयन के साथ डिफ़ॉल्ट कार्यान्वयन का उपयोग करने की अनुमति देते हैं (बस घोषणा को समाप्त करें
=default
)।
इस उत्तर को इस प्रकार सुधारा जा सकता है:
- अधिक उदाहरण कोड जोड़ें
- "आंतरिक क्रॉस-रेफरेंस वाले ऑब्जेक्ट" मामले का चित्रण करें
- कुछ लिंक जोड़ें
यदि आपके पास एक वर्ग है जिसने गतिशील रूप से सामग्री आवंटित की है। उदाहरण के लिए आप एक पुस्तक का शीर्षक चार * के रूप में संग्रहीत करते हैं और शीर्षक को नए के साथ सेट करते हैं, प्रतिलिपि काम नहीं करेगी।
आपको एक कॉपी कंस्ट्रक्टर लिखना होगा जो करता है title = new char[length+1]
और फिर strcpy(title, titleIn)
। कॉपी कंस्ट्रक्टर सिर्फ एक "उथला" कॉपी करेगा।
कॉपी कन्स्ट्रक्टर को उस समय कहा जाता है जब कोई ऑब्जेक्ट या तो वैल्यू द्वारा पास किया जाता है, वैल्यू द्वारा लौटाया जाता है या स्पष्ट रूप से कॉपी किया जाता है। यदि कोई कॉपी कंस्ट्रक्टर नहीं है, तो c ++ एक डिफॉल्ट कॉपी कंस्ट्रक्टर बनाता है जो उथली प्रति बनाता है। यदि ऑब्जेक्ट में डायनामिक रूप से आवंटित मेमोरी के लिए कोई पॉइंटर्स नहीं है, तो उथला कॉपी करेगा।
आइए नीचे कोड स्निपेट पर विचार करें:
class base{
int a, *p;
public:
base(){
p = new int;
}
void SetData(int, int);
void ShowData();
base(const base& old_ref){
//No coding present.
}
};
void base :: ShowData(){
cout<<this->a<<" "<<*(this->p)<<endl;
}
void base :: SetData(int a, int b){
this->a = a;
*(this->p) = b;
}
int main(void)
{
base b1;
b1.SetData(2, 3);
b1.ShowData();
base b2 = b1; //!! Copy constructor called.
b2.ShowData();
return 0;
}
Output:
2 3 //b1.ShowData();
1996774332 1205913761 //b2.ShowData();
b2.ShowData();
जंक आउटपुट देता है क्योंकि स्पष्ट रूप से डेटा की प्रतिलिपि करने के लिए कोई कोड नहीं लिखा गया है जिसके साथ एक उपयोगकर्ता-परिभाषित कॉपी-कंस्ट्रक्टर है। इसलिए कंपाइलर समान नहीं बनाता है।
बस इस ज्ञान को आप सभी के साथ साझा करने के बारे में सोचा, हालाँकि आप में से अधिकांश इसे पहले से ही जानते हैं।
चीयर्स ... हैप्पी कोडिंग !!!