2008-12-02 8 views
105

वस्तु ++ मैं एक सी प्रोग्रामर सी को समझने के लिए ++ की कोशिश कर रहा हूँ।सी आरंभ होने

Dog* sparky = new Dog(); 

जिसका मतलब है कि अगर बाद में आप क्या करेंगे:

delete sparky; 

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

Dog sparky; 

के बजाय ऊपर का उपयोग करें और नाशक दायरे से बाहर चला जाता है एक बार स्पार्की कहा जा जाने के लिए किसी भी कारण है?

धन्यवाद!

उत्तर

152

पर इसके विपरीत, आप हमेशा हद तक ढेर चाहिए पसंद करते आवंटन, कि एक सामान्य नियम के रूप में, आप नए कभी नहीं होना चाहिए/अपने उपयोगकर्ता कोड में हटा दें।

आप कहते हैं के रूप में, जब चर ढेर पर घोषित किया जाता है, इसके नाशक स्वचालित रूप से कहा जाता है, जब यह गुंजाइश है, जो ट्रैकिंग संसाधन जीवन और परहेज लीक के लिए अपने मुख्य उपकरण है से बाहर चला जाता है है।

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

इस मुहावरे के लिए सबसे आम नाम RAII

इसके अलावा स्मार्ट सूचक वर्ग है जो जब आप एक समर्पित आरए II वस्तु के बाहर नए के साथ कुछ आवंटित करने के लिए क्या है दुर्लभ मामलों पर जिसके परिणामस्वरूप संकेत रैप करने के लिए उपयोग किया जाता है पर गौर है। इसके बजाय आप पॉइंटर को एक स्मार्ट पॉइंटर पर पास करते हैं, जो उसके जीवनकाल को ट्रैक करता है, उदाहरण के लिए संदर्भ गिनती के द्वारा, और आखिरी संदर्भ दायरे से बाहर होने पर विनाशक को बुलाता है।मानक पुस्तकालय में std::unique_ptr सरल स्कोप-आधारित प्रबंधन के लिए है, और std::shared_ptr जो साझा स्वामित्व को लागू करने के लिए संदर्भ गणना करता है।

कई ट्यूटोरियल जैसे ...

तो क्या आपको पता चला है कि ज्यादातर ट्यूटोरियल चूसना है एक टुकड़ा का उपयोग कर वस्तु इन्स्टेन्शियशन का प्रदर्शन। ;) अधिकतर ट्यूटोरियल आपको लुसी सी ++ प्रथाओं को सिखाते हैं, जिसमें आवश्यकतानुसार चर बनाने के लिए नए/डिलीट को कॉल करना शामिल है, और आपको अपने आवंटन के जीवनकाल को ट्रैक करने में कठिनाई होती है।

+0

कच्चे पॉइंटर्स उपयोगी होते हैं जब आप auto_ptr-like semantics (स्वामित्व स्थानांतरित करना) चाहते हैं, लेकिन गैर-फेंकने वाले स्वैप ऑपरेशन को बनाए रखें, और संदर्भ गणना के ओवरहेड को नहीं चाहते हैं। एज मामला शायद, लेकिन उपयोगी है। –

+2

यह एक सही उत्तर है, लेकिन कारण मैं कभी भी ढेर पर वस्तुओं को बनाने की आदत में नहीं पहुंचूंगा क्योंकि यह कभी भी स्पष्ट नहीं है कि वह वस्तु कितनी बड़ी होगी। आप बस एक स्टैक-ओवरफ्लो अपवाद मांग रहे हैं। – dviljoen

+3

ग्रेग: निश्चित रूप से। लेकिन जैसा कि आप कहते हैं, एक बढ़त मामला। आम तौर पर, पॉइंटर्स से बचा जाता है। लेकिन वे एक कारण के लिए भाषा में हैं, कोई इनकार नहीं करते हैं। :) dviljoen: यदि ऑब्जेक्ट बड़ा है, तो आप इसे एक RAII ऑब्जेक्ट में लपेटते हैं, जिसे स्टैक पर आवंटित किया जा सकता है, और इसमें हेप-आवंटित डेटा के लिए एक पॉइंटर होता है। – jalf

13

ठीक है, सूचक का उपयोग करने का कारण बिल्कुल वही होगा जो malloc के साथ आवंटित सी में पॉइंटर्स का उपयोग करने का कारण होगा: यदि आप चाहते हैं कि आपकी ऑब्जेक्ट आपके चर से अधिक लाइव रहें!

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

5

एकमात्र कारण मैं के बारे में चिंता चाहते हैं कि कुत्ता अब, ढेर पर आवंटित किया जाता है ना कि स्टेक से है। तो अगर कुत्ता आकार मेगाबाइट है, तो आप एक समस्या हो सकती है,

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

1

नई करने का कोई कारण (ढेर पर) जब आप ढेर पर आवंटित कर सकते हैं (जब तक कि किसी कारण से आप एक छोटे से ढेर हो गया और ढेर का उपयोग करना चाहते कर दिया है।

आप प्रयोग करने पर विचार करना चाह सकते हैं यदि आप ढेर पर आवंटित करना चाहते हैं तो एक मानक_ptr (या इसके रूपों में से एक)। यदि आपके द्वारा साझा_प्टर के सभी संदर्भ अस्तित्व से बाहर हो गए हैं तो यह आपके लिए हटाएगा।

+0

उदाहरण के लिए, मुझे जवाब देखने के कई कारण हैं। – dangerousdave

+0

मुझे लगता है कि आप ऑब्जेक्ट को फ़ंक्शन के दायरे से बाहर निकलना चाहते हैं, लेकिन ओपी ऐसा नहीं लगता था कि वे ऐसा करने की कोशिश कर रहे थे। –

7

एक बहुत ही महत्वपूर्ण अचल संपत्ति के रूप में ढेर का इलाज करें और इसे बहुत ही समझदारी से उपयोग करें। मूल अंगूठे नियम स्टैक का उपयोग करना संभव है जब भी संभव हो और जब भी कोई अन्य तरीका न हो तो ढेर का उपयोग करें। स्टैक पर ऑब्जेक्ट्स आवंटित करके आप कई लाभ प्राप्त कर सकते हैं जैसे:

(1)। अपवाद

(2) के मामले में आपको अवांछित स्टैक के बारे में चिंता करने की आवश्यकता नहीं है। आपको अपने हीप मैनेजर द्वारा आवश्यक स्थान से अधिक स्थान आवंटित करने के कारण स्मृति विखंडन के बारे में चिंता करने की आवश्यकता नहीं है।

+1

वस्तु के आकार के रूप में कुछ विचार होना चाहिए। ढेर आकार सीमित है, इसलिए बहुत बड़ी वस्तुओं को आवंटित ढेर होना चाहिए। – Adam

+1

@ एडम मुझे लगता है कि नवीन अभी भी सही है। यदि आप ढेर पर एक बड़ी वस्तु डाल सकते हैं, तो इसे करें, क्योंकि यह बेहतर है। ऐसे कई कारण हैं जिनसे आप शायद नहीं कर सकते हैं। लेकिन मुझे लगता है कि वह सही है। – McKay

14

मैंने उन लोगों से इस विरोधी पैटर्न को देखा है जो पता-ऑपरेटर के पते को नहीं मिला है। अगर उन्हें एक पॉइंटर के साथ एक फ़ंक्शन कॉल करने की आवश्यकता है, तो वे हमेशा ढेर पर आवंटित करेंगे ताकि उन्हें पॉइंटर मिल सके।

void FeedTheDog(Dog* hungryDog); 

Dog* badDog = new Dog; 
FeedTheDog(badDog); 
delete badDog; 

Dog goodDog; 
FeedTheDog(&goodDog); 
+0

वैकल्पिक रूप से: शून्य फ़ीडTheDog (कुत्ता और भूख डॉग); कुत्ता कुत्ता; FeedTheDog (कुत्ता); –

+1

@ सॉटललांगम सच है, लेकिन वह मुद्दा नहीं था जिसे मैं बनाने की कोशिश कर रहा था। कभी-कभी आप ऐसे फ़ंक्शन को कॉल कर रहे हैं जो उदाहरण के लिए एक वैकल्पिक नल पॉइंटर लेता है, या हो सकता है कि यह एक मौजूदा एपीआई है जिसे बदला नहीं जा सकता है। –

-4

मुझे विजुअल स्टूडियो में एक ही समस्या थी। आपको इसका उपयोग करना होगा:

yourClass-> classMethod();

बजाय:

yourClass.classMethod();

+2

यह सवाल का जवाब नहीं देता है। आप ध्यान दें कि किसी को ढेर पर आवंटित ऑब्जेक्ट (ऑब्जेक्ट के पॉइंटर के माध्यम से) और स्टैक पर आवंटित ऑब्जेक्ट तक पहुंचने के लिए विभिन्न वाक्यविन्यास का उपयोग करना होगा। –

0

एक अतिरिक्त कारण है, जिसने किसी और का उल्लेख नहीं किया है, आप अपनी वस्तु को गतिशील रूप से क्यों चुन सकते हैं। गतिशील, ढेर आधारित वस्तुएं आपको polymorphism का उपयोग करने की अनुमति देती हैं।

+2

आप ढेर आधारित वस्तुओं के साथ पॉलिमॉर्फिक रूप से भी काम कर सकते हैं, मुझे इस संबंध में स्टैक/और ढेर आवंटित वस्तुओं के बीच कोई अंतर नहीं दिखता है। उदाहरण: शून्य MyFunc() {कुत्ता कुत्ता; कुत्ते को खिलाओ); } शून्य फ़ीड (पशु और पशु) {ऑटो भोजन = पशु-> GetFavouriteFood(); PutFoodInBowl (खाद्य); } // इस उदाहरण में GetFavouriteFood एक आभासी कार्य हो सकता है कि प्रत्येक जानवर अपने स्वयं के कार्यान्वयन के साथ ओवरराइड करता है। यह पॉलिमॉर्फिक रूप से काम करेगा जिसमें कोई ढेर शामिल नहीं है। –

+0

-1: बहुरूपता केवल एक संदर्भ या सूचक की आवश्यकता है; यह अंतर्निहित उदाहरण की भंडारण अवधि का पूरी तरह से अज्ञेयवादी है। –

+0

@underscore_d "एक सार्वभौमिक आधार वर्ग का उपयोग लागत का तात्पर्य है: ऑब्जेक्ट्स को ढीला होना चाहिए-पॉलिमॉर्फिक होने के लिए आवंटित;" - bjarne stroustrup http://www.stroustrup.com/bs_faq2.html#object – dangerousdave

19

हालांकि स्टैक पर चीजें रखने से आवंटन और स्वत: मुक्त करने वाली चीजों के मामले में इसका लाभ हो सकता है, इसके कुछ नुकसान हैं।

  1. आप ढेर पर भारी वस्तुओं आवंटित करने के लिए नहीं चाहते हो सकता है।

  2. रनटाइम प्रकार! इस कोड पर विचार करें:

#include <iostream> 

class A { 
public: 
    virtual void f(); 
}; 

class B : public A { 
public: 
    virtual void f(); 
}; 

void A::f() {cout << "A\n";} 
void B::f() {cout << "B\n";} 

int main(void) { 
    A *a = new B(); 
    a->f(); 
    delete a; 
    return 0; 
} 

यह स्पष्ट रूप से प्रिंट होगा "बी \ n"। अब देखते हैं जब ढेर का उपयोग कर क्या होता है की सुविधा देता है:

int main(void) { 
    A a = B(); 
    a->f(); 
    return 0; 
} 

यह होगा प्रिंट "एक \ n" (सी ++ लोग obviosly के लिए)। और यह उन लोगों के लिए सहज नहीं हो सकता है जो जावा या अन्य ओओपी शैली भाषाओं के साथ समान हैं। कारण यह है कि आपके पास बी के उदाहरण पर कोई सूचक नहीं है। लेकिन बी का एक उदाहरण बनाया गया है और फिर ए के एक उदाहरण में कॉपी किया गया है (जो कि पूरी तरह से नव निर्मित है)।

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

+1

-1: लोग मूल्य अर्थशास्त्र और सरल तथ्य को समझने में सक्षम नहीं हैं कि बहुरूपता को संदर्भ/सूचक (ऑब्जेक्ट स्लाइसिंग से बचने के लिए) की आवश्यकता होती है, इस भाषा के साथ किसी प्रकार का मुद्दा नहीं बनता है। सी ++ की शक्ति को सिर्फ एक दोष नहीं माना जाना चाहिए क्योंकि कुछ लोग अपने मूल नियम नहीं सीख सकते हैं। –

+0

सिर के लिए धन्यवाद। मुझे पॉइंटर या संदर्भ के बजाय ऑब्जेक्ट लेने के तरीके के साथ समान समस्या थी, और यह कितना गड़बड़ था। ऑब्जेक्ट के अंदर पॉइंटर्स हैं और इस प्रतिलिपि के कारण वे बहुत जल्द हटा दिए गए हैं। – BoBoDev

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