C ++ ऑब्जेक्ट इंस्टेंटेशन


113

मैं एक C प्रोग्रामर हूं, जो C ++ को समझने की कोशिश कर रहा है। कई ट्यूटोरियल स्निपेट का उपयोग करके ऑब्जेक्ट इंस्टेंटेशन को प्रदर्शित करते हैं जैसे:

Dog* sparky = new Dog();

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

delete sparky;

जो समझ में आता है। अब, मामले में जब गतिशील मेमोरी आवंटन अनावश्यक है, तो क्या इसके बजाय उपरोक्त उपयोग करने का कोई कारण है

Dog sparky;

और एक बार स्पार्कली स्कोप के दायरे से बाहर जाने पर विध्वंसक कहलाता है?

धन्यवाद!

जवाबों:


166

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

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

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

इस मुहावरे का सबसे आम नाम RAII है

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

कई ट्यूटोरियल ऐसे स्निपेट का उपयोग करके ऑब्जेक्ट इंस्टेंशन को प्रदर्शित करते हैं जैसे ...

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


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

2
यह एक सही उत्तर है, लेकिन इसका कारण मैं कभी भी स्टैक पर ऑब्जेक्ट बनाने की आदत में नहीं पड़ूंगा क्योंकि इसकी पूरी तरह से स्पष्ट नहीं है कि वह वस्तु कितनी बड़ी होगी। आप केवल स्टैक-ओवरफ़्लो अपवाद के लिए पूछ रहे हैं।
14

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

3
@dviljoen: नहीं, मैं नहीं। C ++ कंपाइलर अनावश्यक रूप से ऑब्जेक्ट को ब्लोट नहीं करते हैं। आमतौर पर आप जो सबसे बुरा देखेंगे, वह यह है कि यह चार बाइट्स के सबसे नज़दीकी गोल हो जाता है। आमतौर पर, एक पॉइंटर रखने वाला एक क्लास अपने आप में पॉइंटर जितना ही स्पेस लेगा, इसलिए इसमें आपको स्टैक यूज में कुछ भी खर्च नहीं करना पड़ेगा।
jalf

20

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

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

  2. गतिशील प्रेषण! इस कोड पर विचार करें:

#include <iostream>

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

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

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

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

यह "बी" प्रिंट करेगा। अब देखते हैं कि स्टैक का उपयोग करते समय क्या होता है:

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

यह "ए" प्रिंट करेगा, जो उन लोगों के लिए सहज नहीं हो सकता है जो जावा या अन्य वस्तु उन्मुख भाषाओं से परिचित हैं। कारण यह है कि आपके पास Bअब किसी भी उदाहरण के लिए सूचक नहीं है। इसके बजाय, का एक उदाहरण Bबनाया और aप्रकार के चर के लिए नकल की हैA

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


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

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

@underscore_d मैं सहमत हूं; इस उत्तर के अंतिम पैराग्राफ को हटा दिया जाना चाहिए। इस तथ्य को मत लो कि मैंने इस उत्तर को संपादित किया इसका मतलब है कि मैं इससे सहमत हूं; मैं नहीं चाहता था कि इसमें गलतियाँ पढ़ें।
TamaMcGlinn

@TamaMcGlinn सहमत हुए। अच्छे संपादन के लिए धन्यवाद। मैंने राय वाला हिस्सा हटा दिया।
यूनिवर्स

13

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

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


13

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

void FeedTheDog(Dog* hungryDog);

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

Dog goodDog;
FeedTheDog(&goodDog);

वैकल्पिक रूप से: शून्य फ़ीडडॉग (डॉग एंड हंगडॉग); कुत्ता कुत्ता; FeedTheDog (कुत्ता);
स्कॉट लैंगहम

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

7

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

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

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


1
वस्तु के आकार के अनुसार कुछ विचार होना चाहिए। स्टैक आकार सीमित है, इसलिए बहुत बड़ी वस्तुओं को हीप आवंटित किया जाना चाहिए।
एडम

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

5

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

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


1

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

यदि आप ढेर पर आवंटित करना चाहते हैं, तो आप मानक पुस्तकालय से एक साझा_पट्र (या इसके किसी एक संस्करण) का उपयोग करने पर विचार कर सकते हैं। एक बार जब आप सभी के लिए डिलीट को हैंडल कर लेंगे तो share_ptr के सभी संदर्भ अस्तित्व से बाहर हो जाएंगे।


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

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

0

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


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

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

@underscore_d "एक सार्वभौमिक आधार वर्ग का उपयोग करने का तात्पर्य है लागत: वस्तुओं का ढेर होना चाहिए-बहुरूपी होना;" - ब्रेज़न स्ट्रॉस्ट्रुप stroustrup.com/bs_faq2.html#object
खतरनाक

LOL, मुझे परवाह नहीं है अगर स्ट्रॉस्ट्रुप ने खुद कहा, लेकिन यह उसके लिए एक अविश्वसनीय रूप से शर्मनाक उद्धरण है, क्योंकि वह गलत है। यह अपने आप को परखना मुश्किल नहीं है, आप जानते हैं: स्टैक पर कुछ डेरिव्डा, डेरिवेडब, और डेरिवेडेसी को पलटें; तत्काल आधार को स्टैक-आबंटित सूचक भी देता है और पुष्टि करता है कि आप इसे ए / बी / सी के साथ सीट कर सकते हैं और उनका उपयोग पॉलीमॉर्फिक रूप से कर सकते हैं। दावों के लिए महत्वपूर्ण विचार और मानक संदर्भ लागू करें, भले ही वे भाषा के मूल लेखक द्वारा हों। यहाँ: stackoverflow.com/q/5894775/2757035
underscore_d

इसे इस तरह से रखें, मेरे पास एक ऐसी वस्तु है जिसमें स्वचालित भंडारण अवधि के साथ लगभग प्रत्येक 1000 पॉलीमोर्फिक वस्तुओं के 2 अलग-अलग परिवार हैं। मैं स्टैक पर इस ऑब्जेक्ट को तुरंत लिखता हूं, और उन सदस्यों को संदर्भ या संकेत के माध्यम से संदर्भित करके, यह उन पर बहुरूपता की पूर्ण क्षमताओं को प्राप्त करता है। मेरे कार्यक्रम में कुछ भी गतिशील रूप से / ढेर-आवंटित (ढेर-आवंटित वर्गों के स्वामित्व वाले संसाधनों से अलग) नहीं है, और यह एक आईओटी द्वारा मेरी वस्तु की क्षमताओं को नहीं बदलता है।
अंडरस्कोर_ड

-4

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

yourClass-> classMethod ();

बजाय:

yourClass.classMethod ();


3
इस सवाल का जवाब नहीं है। आप ध्यान दें कि ढेर पर आवंटित वस्तु तक पहुँचने के लिए अलग-अलग सिंटैक्स का उपयोग करना होगा (पॉइंटर से ऑब्जेक्ट तक) और स्टैक पर आवंटित ऑब्जेक्ट।
एलेक्सी इवानोव
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.