2011-02-17 11 views
8

यह मेरी समझ है कि जब आप एक वर्ग की एक प्रति बनाते हैं जो सूचक सूचक को परिभाषित करता है, तो सूचक की प्रतिलिपि बनाई जाती है, लेकिन पॉइंटर इंगित करने वाला डेटा नहीं है।सी ++ कक्षा प्रतिलिपि (सूचक प्रति)

मेरा प्रश्न है: क्या कोई यह मान लेता है कि इस मामले में "पॉइंटर कॉपी" एक ही प्रकार के नए सूचक (गतिशील स्मृति आवंटन) को तुरंत चालू कर रहा है? उदाहरण के लिए, नया पॉइंटर बस एक नया आवंटन है जिसमें मनमाने ढंग से स्मृति पता होता है और किसी को ध्यान देने के लिए ध्यान रखना चाहिए कि उचित सूचक पते पर नया सूचक?

मुझे लगता है कि इस सवाल का एक बहुत ही सरल जवाब है, और मैं अपनी छोटी प्रकृति के लिए क्षमा चाहता हूं, लेकिन मैं पॉइंटर्स को गहरे स्तर पर समझने की कोशिश कर रहा हूं और यह इंटरनेट पर मेरे शोधकर्ताओं पर आया।

सादर,

चाड

+0

पॉइंटर्स और डेटा जो वे इंगित करते हैं वे अलग हैं। मार्टिन के उत्तर को नीचे स्पष्ट करना चाहिए यदि मूलभूत बातों के लिए इसे जांच न करें: http://www.cplusplus.com/doc/tutorial/pointers/ – AJG85

उत्तर

13

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

यदि आपको नई मेमोरी आवंटित करने और डेटा की प्रतिलिपि बनाने की आवश्यकता है तो आपको खुद को कॉपी कन्स्ट्रक्टर में करना होगा। दीप कॉपी - आपको इसे स्वयं करना है

संपादित करें: यह सी ++ के फायदों में से एक है, आप यह तय करने के लिए स्वतंत्र हैं कि प्रतिलिपि कैसे काम करती है। यह हो सकता है कि ऑब्जेक्ट की एक प्रति जो केवल स्मृति तक पहुंच को पढ़ती है, स्मृति की प्रतिलिपि बनाने की लागत से बच सकती है। यदि आप नए ऑब्जेक्ट को लिखने की ज़रूरत है तो आप कक्षाओं को भी कार्यान्वित कर सकते हैं जो केवल मूल डेटा की एक प्रति बनाते हैं।

+1

@ चाड केम्प: 'boost :: shared_ptr' और * स्मार्ट पॉइंटर्स * भी देखें। * उथले प्रतिलिपि * और * गहरी प्रति * के बीच अंतर का भी अध्ययन करें। –

+0

बिल्कुल सही। मैं सभी प्रतिक्रियाओं की सराहना करता हूं! पूरी तरह से स्पष्ट! आप सभी को धन्यवाद। –

1

कॉपी पॉइंटर सटीक उसी पते पर इंगित करेगा। कोई नया आवंटन नहीं है।

0

हां, पॉइंटर्स में केवल मेमोरी पते होते हैं, यदि आप गहरी प्रतिलिपि बनाना चाहते हैं तो आपको प्रतिलिपि बनाने वाले में स्वयं को कोड करने की आवश्यकता है।

यदि आप हमेशा एक ही कक्षा से एक सूचक के माध्यम से उसी प्रकार के डेटा का संदर्भ दे रहे हैं, और वस्तुओं के साथ डेटा की प्रतिलिपि बनाने की आवश्यकता है, तो आप इसे एक साधारण सदस्य बनाने के लिए भी एक सूचक नहीं बना सकते हैं।

5

सबसे पहले, आपकी कक्षा में सूचक स्थिर है, (यानी संकलक जानता है कि इस वर्ग में एक पॉइंटर है और इसका आकार क्या है, इसलिए वर्ग को तत्काल होने पर कोई गतिशील स्मृति आवंटन की आवश्यकता नहीं है)।

यदि आप कक्षा की प्रतिलिपि बनाते हैं (और विशेष प्रतिलिपि निर्माता परिभाषित नहीं हैं) तो नई कक्षा में सूचक पुराने वर्ग में सूचक के रूप में स्मृति में एक ही स्थान पर इंगित करेगा। स्पष्ट करने के लिए:

#include <iostream> 

class A { 
    public: 
     int *p; 
}; 

int main() { 
    A a,b; 
    a.p = new int(10); 
    b = a; 

    std::cout << "*(a.p) = " << *(a.p) << std::endl; // 10 
    std::cout << "*(b.p) = " << *(b.p) << std::endl; // 10 

    *(b.p) = 3; 

    std::cout << "*(a.p) = " << *(a.p) << std::endl; // 3 
    std::cout << "*(b.p) = " << *(b.p) << std::endl; // 3 

    return 0; 
} 

अब अगर आप नया स्मृति को आबंटित करने जब नकल, आप एक प्रति निर्माता और एक प्रति असाइनमेंट निर्माता लिखने की ज़रूरत हैं:

#include <iostream> 

class A { 
    public: 
     int *p; 

     A() : p(0) {} 
     A(const A& other) { // copy constructor 
      p = new int(*other.p); 
     } 

     A& operator=(const A& other) { // copy assignment constructor 
      // protect against self assignment 
      if (this != &other) { 
       if (p != 0) { 
        *p = *other.p; 
       } else { // p is null - no memory allocated yet 
        p = new int(*other.p); 
       } 
      } 
      return *this; 
     } 

     ~A() { // destructor 
      delete p; 
     } 
}; 


int main() { 
    A a,b; 
    a.p = new int(10); 
    b = a; 

    std::cout << "*(a.p) = " << *(a.p) << std::endl; // 10 
    std::cout << "*(b.p) = " << *(b.p) << std::endl; // 10 

    *(b.p) = 3; 

    std::cout << "*(a.p) = " << *(a.p) << std::endl; // 10 
    std::cout << "*(b.p) = " << *(b.p) << std::endl; // 3 

    return 0; 
} 

जब आप ऐसा करते तो आप भी एक लिखना चाहिए विनाशक (Rule of three देखें), क्योंकि प्रतिलिपि/कॉपी असाइनमेंट कन्स्ट्रक्टर में आवंटित स्मृति को कक्षा नष्ट होने पर डी-आवंटित करने की आवश्यकता है:

2

पॉइंटर्स गतिशील स्मृति आवंटन को तुरंत चालू नहीं करते हैं। पॉइंटर्स और आवंटन पूरी तरह से अलग चीजें हैं।

यदि आप गतिशील आवंटित स्मृति को इंगित करने वाले पॉइंटर की प्रतिलिपि बनाते हैं, तो आपके पास समान आवंटित स्मृति पर इंगित करने वाले दो पॉइंटर्स हैं। चूंकि आपने इसे कॉपी किया है, यह पहले ही मेमोरी ब्लॉक को इंगित करता है। विशेष रूप से, यदि कंपाइलर से उत्पन्न प्रतिलिपि निर्माता का उपयोग करते हैं, तो नया पॉइंटर पुराने सूचक के समान सटीक चीज़ को इंगित करेगा। यदि यह ठीक है तो आपको इसके साथ कुछ भी करने की ज़रूरत नहीं है।

आपको स्मृति को मुक्त करने की समस्या है। इसे दो बार मुक्त करने से आम तौर पर ढेर भ्रष्टाचार हो सकता है, जो बुरा है। मुक्त नहीं होने से यह स्मृति रिसाव का कारण बन जाएगा, जो कुछ परिस्थितियों में स्वीकार्य हो सकता है। इसके साथ अन्य पॉइंटर के माध्यम से इसे मुक्त करना भी समस्याएं पैदा करेगा। इस कारण से, जिन लोगों के पास एक ही मेमोरी के लिए एकाधिक पॉइंटर्स होते हैं वे अक्सर बूस्ट प्रोजेक्ट पर जाते हैं और अपने साझा_प्टर टेम्पलेट का उपयोग करते हैं (जो आगामी नए मानक में होगा, और सबसे अद्यतित सिस्टम में मौजूद है)।

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

संबंधित मुद्दे