सी ++ रणनीति लिखने से पहले कुछ बुनियादी ज्ञान की आवश्यकता होती है, लेकिन कम से कम इन नियमों को जानना आवश्यक है। नीचे सामग्री है।
यदि कोई व्यक्ति खुद को प्रोग्रामर कहता है और स्मृति के बारे में कुछ नहीं जानता है, तो मैं आपको बता सकता हूं कि वह निश्चित रूप से डींग मार रहा है। सी या सी ++ में प्रोग्राम लिखना, स्मृति पर अधिक ध्यान देने की आवश्यकता है, न केवल इसलिए कि स्मृति का उचित आवंटन सीधे कार्यक्रम की दक्षता और प्रदर्शन को प्रभावित करता है, बल्कि मुख्य रूप से, जब हम स्मृति को संचालित करते हैं तो गलती से समस्याएं होती हैं, और कई बार, ये समस्याएं आसानी से पता नहीं चलती हैं, जैसे कि मेमोरी रिसाव, जैसे कि पॉइंटर हैंगिंग। लेखक आज इन समस्याओं से बचने के तरीके पर चर्चा नहीं करना चाहता है, बल्कि सी ++ मेमोरी ऑब्जेक्ट को एक अलग दृष्टिकोण से जानना चाहता है।
हम जानते हैं कि C++ मेमोरी को तीन तार्किक क्षेत्रों में विभाजित करता है: स्टैक, स्टैक और स्टैटिक स्टोरेज ज़ोन। इस प्रकार, मैं उनके बीच स्थित ऑब्जेक्ट्स को स्टैक ऑब्जेक्ट्स, स्टैक ऑब्जेक्ट्स और स्टैटिक ऑब्जेक्ट्स कहता हूं। तो इन विभिन्न मेमोरी ऑब्जेक्ट्स में क्या अंतर है? स्टैक ऑब्जेक्ट्स और स्टैक ऑब्जेक्ट्स के क्या फायदे और नुकसान हैं? स्टैक ऑब्जेक्ट्स या स्टैक ऑब्जेक्ट्स बनाने से कैसे मना किया जाता है? ये आज के विषय हैं।
1 बुनियादी अवधारणाएँ
Type stack_object ;
stack_object एक स्टैक ऑब्जेक्ट है जिसका जीवन काल परिभाषित बिंदु से शुरू होता है और जब यह फ़ंक्शन वापस आता है, तो जीवन समाप्त हो जाता है।
इसके अलावा, लगभग सभी अस्थायी ऑब्जेक्ट्स फ्यूज ऑब्जेक्ट्स हैं। उदाहरण के लिए, निम्न फ़ंक्शन परिभाषाः
Type fun(Type object);
यह फ़ंक्शन कम से कम दो अस्थायी ऑब्जेक्ट उत्पन्न करता है, सबसे पहले, पैरामीटर को मान के रूप में पास किया जाता है, इसलिए एक अस्थायी ऑब्जेक्ट object_copy1 उत्पन्न करने के लिए कॉपी बिल्ड फ़ंक्शन को बुलाया जाता है। फ़ंक्शन के अंदर उपयोग किया जाने वाला ऑब्जेक्ट ऑब्जेक्ट नहीं है, बल्कि ऑब्जेक्ट_कॉपी 1 है, स्वाभाविक रूप से, ऑब्जेक्ट_कॉपी 1 एक फ़िरोज़ा ऑब्जेक्ट है, जो फ़ंक्शन के रिटर्न पर जारी किया जाता है; और यह फ़ंक्शन एक मान रिटर्न है, जो फ़ंक्शन के रिटर्न पर जारी किया जाता है, अगर हम रिटर्न मान अनुकूलन को अनदेखा करते हैं ((एनआरवी), तो एक अस्थायी ऑब्जेक्ट object_copy2 भी उत्पन्न होता है, जो फ़ंक्शन के रिटर्न के बाद एक समय के भीतर जारी किया जाता है। उदाहरण के लिए, एक फ़ंक्शन में निम्न कोड होता हैः
Type tt ,result ; //生成两个栈对象
tt = fun(tt); //函数返回时,生成的是一个临时对象object_copy2
उपरोक्त दूसरे कथन का निष्पादन इस प्रकार है, सबसे पहले फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन फ़ंक्शन
tt = object_copy2 ; //调用赋值运算符
क्या आप देखते हैं? कंपाइलर हमारे लिए इतने सारे अस्थायी ऑब्जेक्ट्स उत्पन्न करता है कि हम इसके बारे में कुछ भी नहीं जानते हैं, और इन अस्थायी ऑब्जेक्ट्स को बनाने के लिए समय और स्थान की लागत बहुत अधिक हो सकती है, इसलिए, आप शायद समझ सकते हैं कि क्यों फ्लैगशिप ऑब्जेक्ट्स के लिए फंक्शन पैरामीटर को पास करने के बजाय निरंतर संदर्भ के साथ पास करना सबसे अच्छा है।
इसके बाद, स्टैक को देखें. स्टैक, जिसे फ्री स्टोरेज जोन भी कहा जाता है, प्रोग्राम निष्पादन के दौरान गतिशील रूप से वितरित किया जाता है, इसलिए इसकी सबसे बड़ी विशेषता गतिशीलता है। सी ++ में, सभी स्टैक ऑब्जेक्ट्स के निर्माण और विनाश के लिए प्रोग्रामर जिम्मेदार है, इसलिए, यदि इसे ठीक से नहीं संभाला जाता है, तो स्मृति समस्याएं होती हैं। यदि स्टैक ऑब्जेक्ट्स को आवंटित किया जाता है, लेकिन रिलीज करना भूल जाता है, तो स्मृति रिसाव होता है; और यदि ऑब्जेक्ट्स को जारी किया गया है, लेकिन संबंधित पॉइंटर को NULL के रूप में सेट नहीं किया गया है, तो यह पॉइंटर एक तथाकथित लटकता हुआ पॉइंटर है, और जब इस पॉइंटर का फिर से उपयोग किया जाता है, तो अवैध पहुंच होती है, जिससे गंभीर होने पर प्रोग्राम क्रैश हो जाता है।
तो, C++ में स्टैक ऑब्जेक्ट्स को कैसे आवंटित किया जाता है? इसका एकमात्र तरीका new () का उपयोग करना है (और निश्चित रूप से, C स्टैक मेमोरी को malloc निर्देशों के साथ भी प्राप्त किया जा सकता है), जब new का उपयोग किया जाता है, तो स्टैक में एक मेमोरी को आवंटित किया जाता है और उस स्टैक ऑब्जेक्ट को इंगित करने वाले पॉइंटर को वापस लौटाया जाता है।
फिर से देखें स्थैतिक भंडारण क्षेत्र. सभी स्थैतिक वस्तुओं, वैश्विक वस्तुओं स्थैतिक भंडारण क्षेत्र में आवंटित कर रहे हैं. वैश्विक वस्तुओं के बारे में, यह मुख्य () फ़ंक्शन के निष्पादन से पहले ही आवंटित किया गया है. वास्तव में, मुख्य () फ़ंक्शन में प्रदर्शित कोड निष्पादन से पहले, एक कंपाइलर द्वारा उत्पन्न _main () फ़ंक्शन को बुलाया जाता है, जबकि _main () फ़ंक्शन सभी वैश्विक वस्तुओं की संरचना और आरंभिकरण कार्य करता है। मुख्य () फ़ंक्शन के अंत से पहले, सभी वैश्विक वस्तुओं को जारी करने के लिए कंपाइलर द्वारा उत्पन्न exit फ़ंक्शन को बुलाया जाता है। जैसे कि निम्न कोडः
void main(void)
{
... // 显式代码
}
// 实际上转化为这样:
void main(void)
{
_main(); //隐式代码,由编译器产生,用以构造所有全局对象
... // 显式代码
...
exit() ; // 隐式代码,由编译器产生,用以释放所有全局对象
}
इसलिए, यह जानकर, हम कुछ युक्तियों को प्राप्त कर सकते हैं, जैसे कि, मान लीजिए कि हम main () फ़ंक्शन के निष्पादन से पहले कुछ तैयारी करना चाहते हैं, तो हम इन तैयारी को एक कस्टम वैश्विक ऑब्जेक्ट के निर्माण कार्य में लिख सकते हैं, ताकि main () फ़ंक्शन के स्पष्ट कोड के निष्पादन से पहले, इस वैश्विक ऑब्जेक्ट के निर्माण कार्य को बुलाया जाए, और अपेक्षित कार्रवाई की जाए, और इस तरह हमारा उद्देश्य पूरा हो जाए। हमने अभी कहा था कि स्थैतिक भंडारण क्षेत्र में वैश्विक ऑब्जेक्ट, तो, स्थैतिक स्थैतिक ऑब्जेक्ट? स्थैतिक स्थैतिक ऑब्जेक्ट भी आमतौर पर फ़ंक्शन में परिभाषित किया जाता है, जैसे कि ऑब्जेक्ट, केवल इसके सामने एक से अधिक static कुंजी शब्द होते हैं। स्थैतिक स्थैतिक ऑब्जेक्ट का जीवनकाल उस समय से होता है जब फ़ंक्शन को पहली बार बुलाया जाता है, या अधिक सटीक रूप से, जब स्थैतिक ऑब्जेक्ट को पहली बार निष्पादित किया जाता है, जब यह स्थैतिक कोड उत्पन्न होता है, और जब तक यह समाप्त नहीं होता है, तब तक यह पूरी प्रक्रिया नष्ट हो जाती है।
एक अन्य प्रकार का स्थिर वस्तु है, जो कि कक्षा का एक स्थिर सदस्य है। इस स्थिति को ध्यान में रखते हुए, कुछ अधिक जटिल समस्याएं हैं।
प्रथम प्रश्न कक्षा के स्थैतिक सदस्य वस्तुओं का जीवनकाल है, कक्षा के स्थैतिक सदस्य वस्तुओं का जन्म प्रथम वर्ग वस्तु के जन्म के साथ होता है, और पूरी प्रक्रिया के अंत में समाप्त हो जाता है। यानी, ऐसी स्थिति मौजूद है, जिसमें हम एक वर्ग को परिभाषित करते हैं, जिसमें कक्षा में एक स्थैतिक वस्तु सदस्य है, लेकिन प्रक्रिया के निष्पादन के दौरान, यदि हमने उस वर्ग की कोई भी वस्तु नहीं बनाई है, तो उस स्थैतिक वस्तु का उत्पादन नहीं किया जाएगा जो उस वर्ग में शामिल है। इसके अलावा, यदि एक से अधिक कक्षा की वस्तु बनाई गई है, तो ये सभी वस्तुएं उस स्थैतिक वस्तु के सदस्य को साझा करती हैं।
दूसरा सवाल यह है कि जबः
class Base
{
public:
static Type s_object ;
}
class Derived1 : public Base / / 公共继承
{
... // other data
}
class Derived2 : public Base / / 公共继承
{
... // other data
}
Base example ;
Derivde1 example1 ;
Derivde2 example2 ;
example.s_object = …… ;
example1.s_object = …… ;
example2.s_object = …… ;
ऊपर दिए गए तीन blackbody statements पर ध्यान दें, क्या वे s_object को एक ही ऑब्जेक्ट के रूप में access करते हैं? जवाब है, हाँ, वे वास्तव में एक ही ऑब्जेक्ट को access करते हैं, जो कि सच नहीं लगता है, है ना? लेकिन यह सच है, आप इसे अपने आप एक सरल कोड लिखकर verify कर सकते हैं। मैं यह समझाने के लिए कर रहा हूँ कि ऐसा क्यों होता है? हम जानते हैं कि जब Derived1 जैसे एक class, Base जैसे दूसरे class से inherit करता है, तो हम एक Derived1 object को एक Base-type object के रूप में देख सकते हैं, जो कि एक subjectobject है। एक Derived1 object का rough memory layout इस प्रकार हैः
आइए सोचें कि जब हम एक Derived1 प्रकार के ऑब्जेक्ट को एक फ़ंक्शन को देते हैं जो एक गैर-संदर्भित Base प्रकार के पैरामीटर को स्वीकार करता है, तो कटौती कैसे होती है? यकीन है कि अब आप जानते हैं कि यह केवल Derived1 प्रकार के ऑब्जेक्ट से उप-ऑब्जेक्ट को हटा देता है, और सभी अन्य डेटा सदस्यों को अनदेखा करता है जो Derived1 को अनुकूलित करता है, और फिर इस उप-ऑब्जेक्ट को फ़ंक्शन में पास करता है (वास्तव में, फ़ंक्शन इस उप-ऑब्जेक्ट की एक प्रतिलिपि का उपयोग करता है) ।
सभी विरासत में प्राप्त Base प्रकार की व्युत्पन्न वर्ग की वस्तुओं में एक Base-type subobject शामिल है (जिस पर एक derived1 ऑब्जेक्ट की कुंजी Base-type pointer द्वारा इंगित की जा सकती है, जो स्वाभाविक रूप से एक बहुपद कुंजी है), और सभी subobject और सभी Base-type ऑब्जेक्ट एक ही s_object ऑब्जेक्ट का उपयोग करते हैं, और स्वाभाविक रूप से, Base-type से व्युत्पन्न पूरे विरासत प्रणाली वर्ग के सभी उदाहरण एक ही s_object ऑब्जेक्ट का उपयोग करते हैं। उपर्युक्त उदाहरण, उदाहरण 1, उदाहरण 2 का ऑब्जेक्ट लेआउट निम्न चित्र में दिखाया गया हैः
2 तीन मेमोरी ऑब्जेक्ट की तुलना
वस्तुओं का लाभ यह है कि वे स्वचालित रूप से उचित समय पर उत्पन्न होते हैं और उचित समय पर स्वचालित रूप से नष्ट हो जाते हैं, प्रोग्रामर को परेशान करने की आवश्यकता नहीं होती है; और वस्तुओं की निर्माण की गति आमतौर पर ढेर वस्तुओं की तुलना में तेज होती है, क्योंकि ढेर वस्तुओं को आवंटित करते समय, ऑपरेटर new ऑपरेशन को बुलाया जाता है, ऑपरेटर new मेमोरी स्पेस सर्च एल्गोरिथ्म का उपयोग करता है, और यह खोज प्रक्रिया बहुत समय लेने वाली हो सकती है, लेकिन वस्तुओं को उत्पन्न करने में इतनी परेशानी नहीं होती है, इसे केवल शीर्ष सूचक को स्थानांतरित करने की आवश्यकता होती है। हालांकि, ध्यान दें कि आमतौर पर अंतरिक्ष की क्षमता अपेक्षाकृत छोटी होती है, आमतौर पर 1MB 2MB, इसलिए बड़े आकार के आइटम में आवंटित करने के लिए उपयुक्त नहीं होते हैं। विशेष रूप से ध्यान दें कि आवर्ती फ़ंक्शंस में की आवश्यकता का उपयोग न करें, क्योंकि आवर्ती कॉल की गहराई के साथ, बढ़ी हुई जगह भी रैखिकता में वृद्धि करती है, जब स्थान की आवश्यकता नहीं होती
स्टैक ऑब्जेक्ट्स, उनके निर्माण और विनाश के समय को प्रोग्रामर द्वारा परिभाषित किया जाना चाहिए, अर्थात, प्रोग्रामर के पास स्टैक ऑब्जेक्ट्स के जीवन पर पूर्ण नियंत्रण होता है। हमें अक्सर ऐसी वस्तुओं की आवश्यकता होती है, उदाहरण के लिए, हमें एक ऑब्जेक्ट बनाने की आवश्यकता होती है, जिसे कई कार्यों द्वारा पहुँचा जा सकता है, लेकिन इसे सार्वभौमिक नहीं बनाना चाहते हैं, तो इस समय एक स्टैक ऑब्जेक्ट बनाना निश्चित रूप से एक अच्छा विकल्प है, और फिर प्रत्येक फ़ंक्शन के बीच इस स्टैक ऑब्जेक्ट के पॉइंटर को पारित करना, जिससे उस ऑब्जेक्ट के साझाकरण को प्राप्त किया जा सके। इसके अलावा, स्टैक की क्षमता बहुत अधिक है, जो कि स्टैक स्पेस की तुलना में अधिक है। वास्तव में, जब भौतिक मेमोरी पर्याप्त नहीं होती है, और यदि इस समय नए स्टैक ऑब्जेक्ट उत्पन्न करने की आवश्यकता होती है, तो आमतौर पर ऑपरेशन के दौरान कोई त्रुटि उत्पन्न नहीं होती है, लेकिन सिस्टम वर्चुअल मेमोरी का उपयोग वास्तविक भौतिक मेमोरी को बढ़ाने के लिए किया जाता है।
अब हम static objects को देखेंगे.
सबसे पहले, सार्वभौमिक ऑब्जेक्ट। सार्वभौमिक ऑब्जेक्ट कक्षाओं के बीच और फ़ंक्शन के बीच संचार के लिए सबसे सरल तरीका प्रदान करते हैं, हालांकि यह सुरुचिपूर्ण नहीं है। आम तौर पर, पूरी तरह से ऑब्जेक्ट-उन्मुख भाषाओं में, सार्वभौमिक ऑब्जेक्ट मौजूद नहीं हैं, जैसे कि सी #, क्योंकि सार्वभौमिक ऑब्जेक्ट का अर्थ है असुरक्षित और उच्च-संयोजन, और कार्यक्रम में बहुत अधिक उपयोग करने से कार्यक्रम की मजबूती, स्थिरता, रखरखाव और बहुमुखी प्रतिभा को काफी कम कर दिया जाएगा। सी ++ भी पूरी तरह से सार्वभौमिक ऑब्जेक्ट को समाप्त कर सकता है, लेकिन अंततः नहीं, मुझे लगता है कि सी के साथ संगतता के लिए एक कारण है।
इसके बाद, कक्षा के स्थैतिक सदस्य हैं, जैसा कि ऊपर उल्लेख किया गया है, मूल वर्ग और उसकी व्युत्पन्न कक्षा के सभी ऑब्जेक्ट इस स्थैतिक सदस्य ऑब्जेक्ट को साझा करते हैं, इसलिए जब इन वर्गों के बीच या इन वर्ग ऑब्जेक्ट्स के बीच डेटा साझा करने या संवाद करने की आवश्यकता होती है, तो यह निश्चित रूप से एक अच्छा विकल्प है।
इसके बाद स्थैतिक स्थानीय ऑब्जेक्ट हैं, जो मुख्य रूप से उस ऑब्जेक्ट की मध्यवर्ती स्थिति को संरक्षित करने के लिए उपयोग किए जाते हैं, जिसमें फ़ंक्शन को बार-बार बुलाया जाता है, जिनमें से एक सबसे प्रमुख उदाहरण पुनरावर्ती फ़ंक्शन है, हम सभी जानते हैं कि पुनरावर्ती फ़ंक्शन स्वयं को बुलाता है। यदि पुनरावर्ती फ़ंक्शन में एक गैर-स्थैतिक स्थानीय ऑब्जेक्ट को परिभाषित किया जाता है, तो जब पुनरावर्ती की संख्या काफी बड़ी होती है, तो व्यय भी बहुत अधिक होता है। ऐसा इसलिए है क्योंकि गैर-स्थैतिक स्थानीय ऑब्जेक्टों को लूप ऑब्जेक्ट कहा जाता है, प्रत्येक पुनरावर्ती कॉल के लिए, एक ऐसा ऑब्जेक्ट उत्पन्न होता है, और हर बार जब यह वापस आ जाता है, तो यह ऑब्जेक्ट जारी किया जाता है, और ऐसा ऑब्जेक्ट केवल वर्तमान कॉल परत तक ही सीमित होता है, जो गहरी अंतर्निहित परतों और अधिक उज्ज्वल बाहरी परतों के लिए अदृश्य होता है। प्रत्येक स्थानीय ऑब्जेक्ट का अपना ऑब्जेक्ट और पैरामीटर होता है।
पुनरावर्ती फ़ंक्शन डिजाइन में, स्थैतिक वस्तुओं को गैर-स्थैतिक स्थानीय वस्तुओं के स्थान पर इस्तेमाल किया जा सकता है, जो न केवल प्रत्येक पुनरावर्ती कॉल और रिटर्न पर उत्पन्न और जारी किए गए गैर-स्थैतिक वस्तुओं की लागत को कम करता है, बल्कि स्थैतिक वस्तुओं को पुनरावर्ती कॉल की मध्यवर्ती स्थिति को संरक्षित करने के लिए भी उपयोग किया जा सकता है, और प्रत्येक कॉल परत के लिए सुलभ है।
3 अनपेक्षित फसल
जैसा कि पहले बताया गया है, एक लीक ऑब्जेक्ट उचित समय पर बनाया जाता है, और फिर उचित समय पर स्वचालित रूप से जारी किया जाता है, यानी लीक ऑब्जेक्ट में स्वचालित प्रबंधन की क्षमता होती है। तो लीक ऑब्जेक्ट स्वचालित रूप से कब जारी किया जाता है? पहला, अपने जीवनकाल के अंत में; दूसरा, जब इसके कार्य में कोई असामान्यता होती है। आप कह सकते हैं कि यह सब सामान्य है, कुछ भी नहीं। हाँ, कुछ भी नहीं। लेकिन जब तक हम थोड़ा और गहराई तक जाते हैं, तब तक शायद कोई अप्रत्याशित फसल हो।
यदि हम संसाधनों को स्टैक ऑब्जेक्ट में कैप्चर करते हैं, और संसाधनों को निकालने के लिए स्टैक ऑब्जेक्ट के कैप्चर में कार्य करते हैं, तो संसाधनों के रिसाव की संभावना बहुत कम हो जाती है, क्योंकि स्टैक ऑब्जेक्ट स्वचालित रूप से संसाधनों को जारी कर सकता है, यहां तक कि जब फ़ंक्शन में कोई असामान्यता होती है। वास्तविक प्रक्रिया यह है कि जब फ़ंक्शन असामान्यताओं को फेंक देता है, तो ऐसा होता है जिसे स्टैक अनवॉन्ड कहा जाता है, यानी स्टैक का रोलआउट होता है। क्योंकि स्टैक ऑब्जेक्ट, प्राकृतिक स्टैक में मौजूद है, स्टैक के रोलआउट के दौरान, स्टैक ऑब्जेक्ट का कैप्चर किया जाता है, जिससे इसके कैप्चर किए गए संसाधनों को मुक्त कर दिया जाता है। जब तक कि यह असामान्यता फिर से कैप्चर फ़ंक्शन के निष्पादन के दौरान वापस नहीं आती है, तो यह बहुत कम संभावना है, इसलिए स्टैक ऑब्जेक्ट को कैप्चर करने के लिए संसाधनों को कैप्चर करने के लिए उपयोग किया जाता है। हम इस तरह के उदाहरणों के
4 स्टैक ऑब्जेक्ट बनाने से मना किया गया
जैसा कि ऊपर उल्लेख किया गया है, यदि आप किसी प्रकार के स्टैक ऑब्जेक्ट के उत्पादन को प्रतिबंधित करने का निर्णय लेते हैं, तो आप स्वयं एक संसाधन पैकेजिंग वर्ग बना सकते हैं, जो केवल स्टैक में उत्पन्न होता है, जो असामान्य परिस्थितियों में पैकेजिंग संसाधनों को स्वचालित रूप से जारी करता है।
तो हम जानते हैं कि स्टैक ऑब्जेक्ट बनाने का एकमात्र तरीका new ऑपरेशन का उपयोग करना है, अगर हम new का उपयोग करने से मना करते हैं तो क्या यह काम नहीं करेगा? आगे, new ऑपरेशन निष्पादित होने पर ऑपरेटर new को बुलाया जाएगा, और ऑपरेटर new को फिर से लोड किया जा सकता है। एक तरीका है, new ऑपरेटर को निजी बनाना, सममित करने के लिए, ऑपरेटर delete को भी निजी के रूप में फिर से लोड करना बेहतर है। अब, आप शायद फिर से सवाल कर रहे हैं, क्या आपको स्टैक ऑब्जेक्ट बनाने के लिए new को कॉल करने की आवश्यकता नहीं है? हाँ, इसकी आवश्यकता नहीं है, क्योंकि स्टैक ऑब्जेक्ट बनाने के लिए मेमोरी की खोज करने की आवश्यकता नहीं है, लेकिन स्टैक को सीधे संपीड़ित करने के लिए स्टैक को संपीड़ित करना है, और new ऑपरेटर का मुख्य कार्य उपयुक्त स्टैक मेमोरी की खोज करना है, स्टैक को स्टैक में स्थान आवंटित करना, जैसा कि ऊपर उल्लेख किया गया है। तो आइए नीचे दिए गए उदाहरण कोड को देखेंः
#include <stdlib.h> //需要用到C式内存分配函数
class Resource ; //代表需要被封装的资源类
class NoHashObject
{
private:
Resource* ptr ;//指向被封装的资源
... ... //其它数据成员
void* operator new(size_t size) //非严格实现,仅作示意之用
{
return malloc(size) ;
}
void operator delete(void* pp) //非严格实现,仅作示意之用
{
free(pp) ;
}
public:
NoHashObject()
{
//此处可以获得需要封装的资源,并让ptr指针指向该资源
ptr = new Resource() ;
}
~NoHashObject()
{
delete ptr ; //释放封装的资源
}
};
NoHashObject अब एक ऐसी क्लास है जो स्टैक ऑब्जेक्ट को प्रतिबंधित करती है, यदि आप निम्न कोड लिखते हैंः
NoHashObject* fp = new NoHashObject() ; // संकलन त्रुटि!
delete fp ;
उपरोक्त कोड संकलन त्रुटि उत्पन्न करता है। ठीक है, अब जब आप जानते हैं कि कैसे एक स्टैक ऑब्जेक्ट को प्रतिबंधित करने के लिए एक वर्ग को डिजाइन करना है, तो आप शायद मेरी तरह ही इस तरह के सवाल पूछते हैं, क्या यह निश्चित नहीं है कि इस प्रकार के स्टैक ऑब्जेक्ट को तब तक उत्पन्न नहीं किया जा सकता है जब तक कि NoHashObject की परिभाषा को बदला नहीं जा सकता है? नहीं, लेकिन ऐसा करने के लिए एक तरीका है, जिसे मैं इसे कहते हैं कि एक पहेली हिंसा हैकिंग विधि। C ++ इतना शक्तिशाली है कि आप इसके साथ जो कुछ भी करना चाहते हैं वह कर सकते हैं। यहां मुख्य रूप से उपयोग की जाने वाली तकनीक एक सूचक प्रकार का बलपूर्वक रूपांतरण है।
void main(void)
{
char* temp = new char[sizeof(NoHashObject)] ;
//强制类型转换,现在ptr是一个指向NoHashObject对象的指针
NoHashObject* obj_ptr = (NoHashObject*)temp ;
temp = NULL ; //防止通过temp指针修改NoHashObject对象
//再一次强制类型转换,让rp指针指向堆中NoHashObject对象的ptr成员
Resource* rp = (Resource*)obj_ptr ;
//初始化obj_ptr指向的NoHashObject对象的ptr成员
rp = new Resource() ;
//现在可以通过使用obj_ptr指针使用堆中的NoHashObject对象成员了
... ...
delete rp ;//释放资源
temp = (char*)obj_ptr ;
obj_ptr = NULL ;//防止悬挂指针产生
delete [] temp ;//释放NoHashObject对象所占的堆空间。
}
उपरोक्त कार्यान्वयन कष्टप्रद है, और यह लगभग व्यावहारिक रूप से उपयोग नहीं किया जाता है, लेकिन मैंने इसे लिखा है, क्योंकि इसे समझना सी ++ मेमोरी ऑब्जेक्ट्स को समझने के लिए फायदेमंद है। उपरोक्त सभी प्रकार के अनिवार्य रूपांतरणों के लिए, इसका मूल क्या है? हम इसे इस तरह समझ सकते हैंः
मेमोरी में डेटा अपरिवर्तित है, और प्रकार हमारे द्वारा पहने गए चश्मे हैं, जब हम एक प्रकार के चश्मे पहनते हैं, तो हम मेमोरी में डेटा की व्याख्या करने के लिए संबंधित प्रकारों का उपयोग करते हैं, इसलिए अलग-अलग व्याख्याओं को अलग-अलग जानकारी मिलती है।
जबरन प्रकार परिवर्तन वास्तव में एक दूसरे चश्मे को बदलने के बाद एक ही स्मृति डेटा को देखने के लिए है।
यह भी ध्यान दिया जाना चाहिए कि विभिन्न कंपाइलरों के लिए ऑब्जेक्ट के सदस्य डेटा का लेआउट अलग-अलग हो सकता है, उदाहरण के लिए, अधिकांश कंपाइलरों ने NoHashObject के ptr पॉइंटर सदस्यों को ऑब्जेक्ट स्पेस के पहले 4 बाइट्स में रखा है ताकि यह सुनिश्चित किया जा सके कि निम्नलिखित कथन के रूपांतरण की कार्रवाई की जा सके जैसा कि हम उम्मीद करते हैंः
Resource* rp = (Resource*)obj_ptr ;
हालांकि, यह जरूरी नहीं है कि सभी कंपाइलरों के साथ ऐसा हो।
यदि हम किसी प्रकार के स्टैक ऑब्जेक्ट्स को उत्पन्न करने के लिए मना कर सकते हैं, तो क्या हम एक ऐसी कक्षा को डिज़ाइन कर सकते हैं जो स्टैक ऑब्जेक्ट्स उत्पन्न नहीं कर सके?
5 प्रजनन वस्तुओं को प्रतिबंधित करना
जैसा कि पहले उल्लेख किया गया है, जब एक लूप ऑब्जेक्ट बनाया जाता है, तो लूप ऑब्जेक्ट को निकालने के लिए लूप पॉइंटर को स्थानांतरित किया जाता है, और फिर इस स्थान पर एक लूप ऑब्जेक्ट बनाने के लिए सीधे संबंधित संरचनात्मक फ़ंक्शन को बुलाया जाता है, और जब फ़ंक्शन वापस आता है, तो यह ऑब्जेक्ट को जारी करने के लिए अपने विच्छेदन फ़ंक्शन को बुलाता है, और फिर लूप ऑब्जेक्ट को पुनर्प्राप्त करने के लिए लूप पॉइंटर को समायोजित करता है। इस प्रक्रिया में ऑपरेटर new/delete ऑपरेशन की आवश्यकता नहीं होती है, इसलिए ऑपरेटर new/delete को निजी के रूप में सेट करना उद्देश्य को पूरा नहीं कर सकता है। निश्चित रूप से ऊपर की व्याख्या से, आपने शायद सोचा होगाः संरचनात्मक फ़ंक्शन या विच्छेदन फ़ंक्शन सेटिंग्स को निजी बनाएं, ताकि सिस्टम संरचनात्मक / विच्छेदन फ़ंक्शन को कॉल न कर सके, और निश्चित रूप से लूप में ऑब्जेक्ट उत्पन्न न हो सके।
ऐसा हो सकता है, और मैं इस तरह के कार्यक्रम को अपनाने की योजना बना रहा हूं। लेकिन इससे पहले, एक बात पर विचार करने की जरूरत है, यह है कि अगर हम निर्माण कार्य को निजी के रूप में सेट करते हैं, तो हम नए के साथ सीधे ढेर ऑब्जेक्ट उत्पन्न नहीं कर सकते हैं, क्योंकि नए ऑब्जेक्ट के लिए स्थान आवंटित करने के बाद अपने निर्माण कार्य को भी बुलाएगा। तो, मैं केवल विच्छेदन समारोह को निजी के रूप में सेट करने की योजना बना रहा हूं। आगे, क्या विच्छेदन समारोह को निजी के रूप में सेट करने के अलावा अन्य प्रभाव होंगे जो कि ऑब्जेक्ट जनरेशन को प्रतिबंधित करेंगे? हाँ, यह भी विरासत को प्रतिबंधित करेगा।
यदि एक वर्ग को एक आधार वर्ग के रूप में उपयोग नहीं किया जाता है, तो आमतौर पर इसका विश्लेषण फ़ंक्शन को निजी घोषित किया जाता है।
इस प्रकार, यदि हम किसी प्रकार की सीमा नहीं लगाते हैं, तो हम इसे संरक्षित घोषित कर सकते हैं, और यह दोनों के लिए अच्छा है।
class NoStackObject
{
protected:
~NoStackObject() { }
public:
void destroy()
{
delete this ;//调用保护析构函数
}
};
इसके बाद, हम NoStackObject वर्ग का उपयोग कर सकते हैं जैसे किः
NoStackObject* hash_ptr = new NoStackObject() ;
… … // hash_ptr निर्देशित ऑब्जेक्ट पर कार्रवाई करें
hash_ptr->destroy() ; अरे, क्या यह थोड़ा अजीब लगता है कि हम एक ऑब्जेक्ट बनाने के लिए new का उपयोग करते हैं, लेकिन इसे हटाने के लिए delete का उपयोग नहीं करते हैं, बल्कि destroy का उपयोग करते हैं। यह स्पष्ट है कि उपयोगकर्ता इस अजीब तरीके से उपयोग करने के लिए उपयोग नहीं किया जाता है। इसलिए, मैंने कॉन्फ़िगरेशन फ़ंक्शन को निजी या संरक्षित करने का फैसला किया है। यह फिर से उस सवाल पर वापस आता है जिसे हमने ऊपर से बचने की कोशिश की थी, यानी new का उपयोग नहीं करना, तो ऑब्जेक्ट कैसे उत्पन्न किया जाए? हम इसे एक अप्रत्यक्ष तरीके से कर सकते हैं, यानी इस वर्ग को एक स्थिर सदस्य फ़ंक्शन प्रदान करना जो विशेष रूप से इस प्रकार के ढेर ऑब्जेक्ट उत्पन्न करने के लिए है।
”` class NoStackObject { protected:
NoStackObject() { }
~NoStackObject() { }
public:
static NoStackObject* creatInstance()
{
return new No