मान लीजिए कि मेरे पास निम्नलिखित कोड हैं:
void* my_alloc (size_t size)
{
return new char [size];
}
void my_free (void* ptr)
{
delete [] ptr;
}
क्या यह सुरक्षित है? या विलोपन ptr
से char*
पहले डाला जाना चाहिए ?
मान लीजिए कि मेरे पास निम्नलिखित कोड हैं:
void* my_alloc (size_t size)
{
return new char [size];
}
void my_free (void* ptr)
{
delete [] ptr;
}
क्या यह सुरक्षित है? या विलोपन ptr
से char*
पहले डाला जाना चाहिए ?
जवाबों:
यह "सुरक्षित" पर निर्भर करता है। यह आमतौर पर काम करेगा क्योंकि आवंटन के बारे में सूचक के साथ ही जानकारी संग्रहीत होती है, इसलिए डीलक्लॉकर इसे सही जगह पर वापस कर सकता है। इस अर्थ में यह "सुरक्षित" है जब तक कि आपका आवंटनकर्ता आंतरिक सीमा टैग का उपयोग करता है। (कई)
हालांकि, जैसा कि अन्य उत्तरों में बताया गया है, एक शून्य पॉइंटर को हटाने से डिस्ट्रक्टर्स को कॉल नहीं किया जाएगा, जो एक समस्या हो सकती है। उस अर्थ में, यह "सुरक्षित" नहीं है।
आप जो कर रहे हैं, उसे करने का कोई अच्छा कारण नहीं है। यदि आप अपने स्वयं के डील-डौल कार्यों को लिखना चाहते हैं, तो आप सही प्रकार के कार्यों को उत्पन्न करने के लिए फ़ंक्शन टेम्प्लेट का उपयोग कर सकते हैं। ऐसा करने का एक अच्छा कारण यह है कि पूल आवंटनकर्ता उत्पन्न होते हैं, जो विशिष्ट प्रकारों के लिए अत्यंत कुशल हो सकते हैं।
जैसा कि अन्य उत्तरों में बताया गया है, यह C ++ में अपरिभाषित व्यवहार है। सामान्य तौर पर यह अपरिभाषित व्यवहार से बचने के लिए अच्छा है, हालांकि यह विषय स्वयं जटिल है और परस्पर विरोधी विचारों से भरा है।
sizeof(T*) == sizeof(U*)
सभी के लिए T,U
सुझाव है कि यह संभव होना चाहिए 1 गैर अस्थायी, void *
आधारित कचरा कलेक्टर कार्यान्वयन। लेकिन तब, जब gc को वास्तव में एक पॉइंटर को डिलीट / फ्री करना होता है, तो यह सवाल उठता है। इसे काम करने के लिए, आपको या तो लैम्ब्डा फंक्शन डिस्ट्रक्टर रैपर (उरग) की आवश्यकता होती है या आपको किसी प्रकार के गतिशील "टाइप द डेटा" प्रकार की आवश्यकता होती है, जो एक प्रकार और कुछ स्टैग के बीच आगे और पीछे की अनुमति देता है।
शून्य सूचक द्वारा हटाना C ++ मानक द्वारा अपरिभाषित है - खंड 5.3.5 / 3 देखें:
पहले विकल्प में (ऑब्जेक्ट हटाएं), यदि ऑपरेंड का स्थिर प्रकार उसके गतिशील प्रकार से अलग है, तो स्थिर प्रकार ऑपरेंड के डायनेमिक प्रकार का एक आधार वर्ग होगा और स्थिर प्रकार में एक वर्चुअल विध्वंसक होगा या व्यवहार अनिर्धारित होगा । दूसरे वैकल्पिक (डिलीट अरेंज) में यदि डिलीट की जाने वाली वस्तु का डायनामिक प्रकार उसके स्टैटिक टाइप से भिन्न होता है, तो व्यवहार अपरिभाषित होता है।
और इसके फुटनोट:
इसका तात्पर्य यह है कि किसी वस्तु को प्रकार void * के सूचक का उपयोग करके हटाया नहीं जा सकता है क्योंकि वहाँ प्रकार की कोई वस्तु शून्य नहीं है
।
NULL
एप्लिकेशन मेमोरी प्रबंधन के लिए कोई अंतर बनाने के साथ एक शून्य पॉइंटर की इंगित पता मेमोरी भरें ?
यह एक अच्छा विचार नहीं है और ऐसा कुछ नहीं है जो आप C ++ में करेंगे। आप बिना किसी कारण के अपने प्रकार की जानकारी खो रहे हैं।
आपके विनाशकर्ता को आपके सरणी में उन वस्तुओं पर नहीं बुलाया जाएगा जिन्हें आप गैर-आदिम प्रकारों के लिए कॉल करते समय हटा रहे हैं।
आपको इसके बजाय नया / हटाना ओवरराइड करना चाहिए।
शून्य को हटाना * संभवतः आपकी स्मृति को सही ढंग से संयोग से मुक्त कर देगा, लेकिन यह गलत है क्योंकि परिणाम अपरिभाषित हैं।
अगर किसी कारण से मेरे लिए अज्ञात है तो आपको अपने पॉइंटर को एक शून्य में संग्रहीत करने की आवश्यकता है * फिर इसे मुक्त करें, आपको मॉलोक और मुफ्त का उपयोग करना चाहिए।
सवाल कोई मतलब नहीं है। आपके भ्रम का कारण आंशिक रूप से उस फूहड़ भाषा के कारण हो सकता है जिसका लोग अक्सर उपयोग करते हैं delete
:
आप delete
एक ऐसी वस्तु को नष्ट करने के लिए उपयोग करते हैं जिसे गतिशील रूप से आवंटित किया गया था। ऐसा करने पर, आप उस ऑब्जेक्ट के लिए एक पॉइंटर के साथ एक डिलीट एक्सप्रेशन बनाते हैं । आप कभी भी "एक पॉइंटर हटाएं" नहीं। आप वास्तव में क्या करते हैं "एक वस्तु को हटा दें जो उसके पते से पहचानी जाती है"।
अब हम देखते हैं कि प्रश्न का कोई मतलब नहीं है: एक शून्य सूचक "किसी वस्तु का पता" नहीं है। यह सिर्फ एक पता है, बिना किसी शब्दार्थ के। यह एक वास्तविक वस्तु के पते से आया हो सकता है, लेकिन यह जानकारी खो जाती है, क्योंकि यह मूल पॉइंटर के प्रकार में एन्कोड किया गया था । ऑब्जेक्ट पॉइंटर को पुनर्स्थापित करने का एकमात्र तरीका शून्य पॉइंटर को ऑब्जेक्ट पॉइंटर पर वापस लाना है (जिससे लेखक को पता चल सके कि पॉइंटर का मतलब क्या है)। void
अपने आप में एक अपूर्ण प्रकार है और इस प्रकार कभी भी ऑब्जेक्ट का प्रकार नहीं होता है, और किसी ऑब्जेक्ट की पहचान करने के लिए एक शून्य पॉइंटर का उपयोग कभी नहीं किया जा सकता है। (वस्तुओं की पहचान उनके प्रकार और उनके पते द्वारा संयुक्त रूप से की जाती है।)
delete
मान एक शून्य सूचक मान हो सकता है, एक पिछले नए-अभिव्यक्ति द्वारा बनाई गई गैर-सरणी ऑब्जेक्ट के लिए एक पॉइंटर, या एक पॉइंटर को एक पॉइंटर इस तरह की वस्तु के आधार वर्ग का प्रतिनिधित्व करने वाला उप-विषय। यदि नहीं, तो व्यवहार अपरिभाषित है। " इसलिए यदि एक संकलक आपके कोड को नैदानिक के बिना स्वीकार करता है, तो यह संकलक में बग के अलावा कुछ भी नहीं है ...
delete void_pointer
। यह अपरिभाषित व्यवहार है। प्रोग्रामर को कभी भी अपरिभाषित व्यवहार नहीं करना चाहिए, भले ही प्रतिक्रिया वही करे जो प्रोग्रामर चाहता था।
यदि आप वास्तव में ऐसा करते हैं, तो मध्यम व्यक्ति ( new
और delete
ऑपरेटरों) को काटकर वैश्विक operator new
और operator delete
सीधे क्यों नहीं बुलाया जाना चाहिए ? (बेशक, अगर आप new
और delete
ऑपरेटरों को साधने की कोशिश कर रहे हैं, तो आपको वास्तव में इसे लागू करना चाहिए operator new
और operator delete
)।
void* my_alloc (size_t size)
{
return ::operator new(size);
}
void my_free (void* ptr)
{
::operator delete(ptr);
}
ध्यान दें कि इसके विपरीत malloc()
, विफलता पर operator new
फेंकता std::bad_alloc
है (या new_handler
यदि पंजीकृत है तो कॉल करता है)।
क्योंकि चार का कोई विशेष विध्वंसक तर्क नहीं है। यह काम नहीं करेगा।
class foo
{
~foo() { printf("huzza"); }
}
main()
{
foo * myFoo = new foo();
delete ((void*)foo);
}
D'ctor बुलाया नहीं जाएगा।
यदि आप शून्य * का उपयोग करना चाहते हैं, तो आप सिर्फ मॉलोक / फ्री का उपयोग क्यों नहीं करते हैं? नया / हटाना केवल मेमोरी प्रबंधन से अधिक है। मूल रूप से, नए / हटाएं एक निर्माता / विध्वंसक को बुलाते हैं और कुछ और चीजें चल रही हैं। यदि आप केवल बिल्ट-इन प्रकारों (जैसे चार *) का उपयोग करते हैं और उन्हें शून्य * के माध्यम से हटाते हैं, तो यह काम करेगा लेकिन फिर भी यह अनुशंसित नहीं है। यदि आप शून्य * का उपयोग करना चाहते हैं तो लब्बोलुआब यह है कि मॉलोक / फ्री का उपयोग करें। अन्यथा, आप अपनी सुविधा के लिए टेम्पलेट फ़ंक्शंस का उपयोग कर सकते हैं।
template<typename T>
T* my_alloc (size_t size)
{
return new T [size];
}
template<typename T>
void my_free (T* ptr)
{
delete [] ptr;
}
int main(void)
{
char* pChar = my_alloc<char>(10);
my_free(pChar);
}
बहुत सारे लोग पहले ही यह कहते हुए टिप्पणी कर चुके हैं कि नहीं, शून्य सूचक को हटाना सुरक्षित नहीं है। मैं इसके साथ सहमत हूं, लेकिन मैं यह भी जोड़ना चाहता था कि यदि आप सन्निहित सरणियों या कुछ इसी तरह का आवंटन करने के लिए शून्य बिंदुओं के साथ काम कर रहे हैं, तो आप ऐसा कर सकते हैं new
ताकि आप delete
सुरक्षित रूप से (साथ, अहम) का उपयोग कर सकें , थोड़ा अतिरिक्त काम)। यह एक शून्य पॉइंटर को मेमोरी क्षेत्र (जिसे 'अखाड़ा' कहा जाता है) को आवंटित करके और फिर नए को अखाड़े को पॉइंटर की आपूर्ति करके किया जाता है। इस अनुभाग को C ++ FAQ में देखें । यह C ++ में मेमोरी पूल को लागू करने के लिए एक सामान्य दृष्टिकोण है।
ऐसा करने का शायद ही कोई कारण हो।
सबसे पहले, आप नहीं जानते कि अगर प्रकार डेटा की, और सब आप जानते हैं कि यह है void*
, तो आप वास्तव में सिर्फ एक typeless के रूप में है कि डेटा के इलाज किया जाना चाहिए ब्लॉब बाइनरी डेटा (के unsigned char*
), और उपयोग malloc
/ free
इससे निपटने के लिए । तरंग डेटा और जैसी चीजों के लिए कभी-कभी इसकी आवश्यकता होती है, जहां आपको void*
सी एपिस को पॉइंटर्स के आसपास से गुजरने की आवश्यकता होती है । कोई बात नहीं।
यदि आप करते हैं डेटा के प्रकार (यानी यह एक ctor / dtor है) पता है, लेकिन किसी कारण से आप एक साथ समाप्त हो गया void*
सूचक (जो भी कारण के लिए आप) तो क्या तुम सच में इसे वापस प्रकार के कास्ट करना चाहिए कि आप इसे जानते हैं हो , और delete
उस पर कॉल करें ।
मैंने अपने ढांचे में शून्य * (उर्फ अज्ञात प्रकार) का उपयोग किया है जबकि कोड प्रतिबिंब और अस्पष्टता के अन्य करतबों में, और अब तक, मुझे किसी भी संकलक से कोई परेशानी (मेमोरी लीक, एक्सेस उल्लंघन, आदि) नहीं हुई है। ऑपरेशन अमानक होने के कारण केवल चेतावनी।
यह एक अज्ञात (शून्य *) को हटाने के लिए पूरी तरह से समझ में आता है। बस सुनिश्चित करें कि सूचक इन दिशानिर्देशों का पालन करता है, या यह समझ बनाना बंद कर सकता है:
1) अज्ञात पॉइंटर को एक प्रकार से इंगित नहीं करना चाहिए जिसमें एक तुच्छ डिकंस्ट्रक्टर है, और इसलिए जब एक अज्ञात पॉइंटर के रूप में डाला जाता है तो इसे कभी भी बंद नहीं होना चाहिए। केवल अज्ञात सूचक को हटाकर इसे मूल प्रकार में वापस डाल दें।
2) क्या स्टैक बाउंड या हीप बाउंड मेमोरी में अज्ञात पॉइंटर के रूप में संदर्भित किया जा रहा है? यदि अज्ञात पॉइंटर स्टैक पर एक उदाहरण को संदर्भित करता है, तो इसे हटा दिया जाना चाहिए!
3) क्या आप 100% सकारात्मक हैं अज्ञात पॉइंटर एक वैध मेमोरी क्षेत्र है? नहीं, तो इसे हटा दिया जाना चाहिए!
सभी में, बहुत कम प्रत्यक्ष काम है जो एक अज्ञात (शून्य *) सूचक प्रकार का उपयोग करके किया जा सकता है। हालांकि, अप्रत्यक्ष रूप से, शून्य * सी ++ डेवलपर्स के लिए एक महान संपत्ति है, जब डेटा अस्पष्टता की आवश्यकता होती है, पर भरोसा करने के लिए।
यदि आप केवल एक बफर चाहते हैं, तो मॉलोक / मुफ्त का उपयोग करें। यदि आपको नया / हटाना आवश्यक है, तो एक तुच्छ आवरण श्रेणी पर विचार करें:
template<int size_ > struct size_buffer {
char data_[ size_];
operator void*() { return (void*)&data_; }
};
typedef sized_buffer<100> OpaqueBuffer; // logical description of your sized buffer
OpaqueBuffer* ptr = new OpaqueBuffer();
delete ptr;
चार के विशेष मामले के लिए।
चार एक आंतरिक प्रकार है जिसमें एक विशेष विध्वंसक नहीं होता है। तो लीक तर्कों में एक लूट है।
sizeof (char) आमतौर पर एक है इसलिए कोई संरेखण तर्क भी नहीं है। दुर्लभ मंच के मामले में जहां आकार (चार) एक नहीं है, वे अपने चार के लिए पर्याप्त संरेखित स्मृति आवंटित करते हैं। तो संरेखण तर्क भी एक लूट है।
इस मामले में मॉलॉक / मुक्त तेजी से होगा। लेकिन आप std :: bad_alloc को जब्त करते हैं और मॉलॉक के परिणाम की जांच करनी होती है। वैश्विक नए और डिलीट ऑपरेटरों को कॉल करना बेहतर हो सकता है क्योंकि यह मध्यम व्यक्ति को बायपास करता है।
new
वास्तव में फेंकने के लिए परिभाषित किया गया है। यह सच नहीं है। यह कंपाइलर और कंपाइलर स्विच डिपेंडेंट है। उदाहरण के लिए देखें MSVC2019 /GX[-] enable C++ EH (same as /EHsc)
स्विच। इसके अलावा एम्बेडेड सिस्टम पर कई लोग C ++ अपवादों के लिए प्रदर्शन करों का भुगतान नहीं करने का विकल्प चुनते हैं। तो "लेकिन आप fordit std :: bad_alloc ..." से शुरू होने वाला वाक्य संदिग्ध है।