विनाशकारी में अजीब दुश्मनी


83

वर्तमान में, मैं स्रोत कोड पढ़ रहा हूं Protocol Buffer, और मुझे यहां एक अजीब enumकोड परिभाषित मिला है

  ~scoped_ptr() {
    enum { type_must_be_complete = sizeof(C) };
    delete ptr_;
  }

  void reset(C* p = NULL) {
    if (p != ptr_) {
      enum { type_must_be_complete = sizeof(C) };
      delete ptr_;
      ptr_ = p;
    }
  }

यहाँ क्यों enum { type_must_be_complete = sizeof(C) };परिभाषित किया गया है? इसका क्या उपयोग है?


2
अगर मैं होना चाहता हूँ कि यह सुनिश्चित करें, तो मैं नहीं बल्कि का उपयोग करेंगे ptr_अपने आप में sizeofके रूप में sizeof(*ptr_)के बजाय sizeof(C)
नवाज

जवाबों:


81

जब यह विध्वंसक संकलित हो जाता है तो C की परिभाषा उपलब्ध है, यह सुनिश्चित करके यह ट्रिक UB से बचा जाता है। अन्यथा संकलन विफल हो जाएगा क्योंकि sizeofअपूर्ण प्रकार (आगे घोषित प्रकार) निर्धारित नहीं किए जा सकते हैं लेकिन पॉइंटर्स का उपयोग किया जा सकता है।

संकलित बाइनरी में, इस कोड को अनुकूलित किया जाएगा और इसका कोई प्रभाव नहीं होगा।

ध्यान दें: अपूर्ण प्रकार को हटाना 5.3.5 / 5 से अपरिभाषित व्यवहार हो सकता है :।

यदि हटाई जा रही वस्तु में विलोपन के बिंदु पर अपूर्ण वर्ग प्रकार है और पूरी कक्षा में एक गैर-तुच्छ विध्वंसक या एक निपटारा कार्य है, तो व्यवहार अपरिभाषित है

g++ यहां तक ​​कि निम्नलिखित चेतावनी भी जारी करता है:

चेतावनी: डिलीट ऑपरेटर के आह्वान में पाई गई संभावित समस्या:
चेतावनी: 'p' में अपूर्ण प्रकार की
चेतावनी है: 'संरचनात्मक C' की आगे की घोषणा


1
"अधूरा प्रकार हटाना अपरिभाषित व्यवहार है" गलत है। यह केवल यूबी है यदि प्रकार में एक गैर-तुच्छ विध्वंसक है। समस्या यह है कि यह छोटा सा ट्रिक पता ठीक है कि अपूर्ण प्रकार को हटाना हमेशा यूबी नहीं होता है, ताकि भाषा इसका समर्थन करे।
चीयर्स एंड हीथ। - अल्फ

धन्यवाद @ Cheersandhth.- अल्फ मेरी बात यह यूबी हो सकती है, इसलिए सामान्य तौर पर कोड की यह लाइन यूबी है। संपादित।
मोहित जैन

32

sizeof(C)एक पूर्ण प्रकार नहीं है, तो संकलन समय पर विफल हो जाएगा C। स्थानीय स्कोप enumको इसमें सेट करने से रनटाइम पर स्टेटमेंट सौम्य हो जाता है।

यह प्रोग्रामर का एक तरीका है जो खुद को खुद से बचाता है: delete ptr_यदि एक गैर-तुच्छ विध्वंसक है तो एक अपूर्ण प्रकार पर बाद का व्यवहार अपरिभाषित है।


1
क्या आप बता सकते हैं कि सी को उस बिंदु पर एक पूर्ण प्रकार होने की आवश्यकता क्यों है - क्या उस deleteपर कॉल करने के लिए पूर्ण प्रकार की परिभाषा होना आवश्यक है? और यदि ऐसा है, तो संकलक इसे वैसे भी क्यों नहीं पकड़ता है?
पीटर हल

1
बचने के बारे में नहीं है C = void? यदि Cकेवल एक अपरिभाषित प्रकार होते, तो deleteकथन पहले से ही विफल नहीं होता ?
केरेक एसबी

1
लगता है कि मोहित जैन को इसका जवाब मिल गया है।
पीटर हुल

1
−1 "स्थानीय स्कोप को इसमें स्थापित करना स्टेटमेंट को रनटाइम पर सौम्य बनाता है।" है, उह, अर्थहीन। मुझे माफ कर दो।
चीयर्स एंड हीथ। - अल्फ़

1
@SteveJessop धन्यवाद जो हिस्सा मुझे याद आ रहा था, वह यह था कि अधूरा टाइप हटाना यूबी है।
पीटर हल

28

इसे समझने के लिए enum, इसके बिना विध्वंसक पर विचार करने के साथ शुरू करें:

~scoped_ptr() {
    delete ptr_;
}

जहां ptr_एक है C*। यदि Cइस बिंदु पर टाइप अधूरा है, यानी वह सब जो कंपाइलर जानता है struct C;, तो (1) एक डिफ़ॉल्ट-जेनरेट डू-नथिंग डिस्ट्रक्टर को इंगित किए गए C उदाहरण के लिए उपयोग किया जाता है। स्मार्ट पॉइंटर द्वारा प्रबंधित ऑब्जेक्ट के लिए यह सही होने की संभावना नहीं है।

यदि एक पॉइंटर को अपूर्ण प्रकार से हटाने पर हमेशा अपरिभाषित व्यवहार होता है, तो मानक को केवल कंपाइलर का निदान करने और विफल होने की आवश्यकता हो सकती है। लेकिन यह अच्छी तरह से परिभाषित है जब वास्तविक विनाशकारी तुच्छ है: ज्ञान जो प्रोग्रामर के पास हो सकता है, लेकिन संकलक के पास नहीं है। भाषा क्यों परिभाषित करती है और इसे अनुमति देती है यह मेरे से परे है, लेकिन C ++ कई प्रथाओं का समर्थन करता है जिन्हें आज सर्वोत्तम प्रथाओं के रूप में नहीं माना जाता है।

एक पूर्ण प्रकार का एक ज्ञात आकार होता है, और इसलिए, sizeof(C)यदि और केवल Cएक पूर्ण प्रकार है - ज्ञात विध्वंसक के साथ संकलित करेगा । तो यह एक गार्ड के रूप में इस्तेमाल किया जा सकता है। एक तरीका बस होगा

(void) sizeof(C);  // Type must be complete

मुझे लगता है कि कुछ संकलक और विकल्पों के साथ संकलक इसे दूर कर देता है इससे पहले कि यह नोटिस कर सकता है कि यह संकलन नहीं करना चाहिए, और यह इस enumतरह के गैर-अनुरूप संकलक व्यवहार से बचने का एक तरीका है:

enum { type_must_be_complete = sizeof(C) };

enumकेवल एक खारिज अभिव्यक्ति के बजाय विकल्प के लिए एक वैकल्पिक स्पष्टीकरण , बस व्यक्तिगत पसंद है।

या जैसा कि जेम्स टी। हगेट इस उत्तर के लिए एक टिप्पणी में सुझाव देते हैं , "संकलन समय पर एक छद्म पोर्टेबल त्रुटि संदेश बनाने का एक तरीका हो सकता है"।


(1) अपूर्ण प्रकार के लिए डिफ़ॉल्ट-जेनरेट डू-नथिंग डिस्ट्रक्टर पुराने के साथ एक समस्या थी std::auto_ptr। यह इतना कपटी था कि इसने PIMPL मुहावरे के बारे में एक GOTW आइटम में अपनी जगह बनाई , जिसे अंतर्राष्ट्रीय C ++ मानकीकरण समिति हर्ब सटर की कुर्सी द्वारा लिखा गया था। बेशक, आजकल जो std::auto_ptrपदावनत है, कोई इसके बजाय किसी अन्य तंत्र का उपयोग करेगा।


4
संकलन समय पर एक छद्म पोर्टेबल त्रुटि संदेश बनाने का एक तरीका हो सकता है।
ब्राइस एम। डेम्पसी

1
मुझे लगता है कि यह उत्तर कोड की प्रेरणा को बहुत अच्छी तरह से बताता है, लेकिन मैं यह जोड़ना चाहूंगा कि (1) कुछ कंपाइलरों ने sizeof(T)संकलन को विफल करने के बजाय अपूर्ण प्रकारों के लिए 0 का मूल्यांकन किया है। हालांकि यह गैर-अनुरूप व्यवहार है। (2) C ++ 11 के बाद से, static_assert((sizeof(T) > 0), "T must be a complete type");एक बेहतर (और मुहावरेदार) समाधान होगा।
5gon12eder

@ 5gon12eder; कृपया C ++ संकलक का एक उदाहरण प्रदान करें जिसमें " sizeof(T)अपूर्ण प्रकारों के लिए 0 का मूल्यांकन " किया गया है।
चीयर्स एंड हीथ। - अल्फ

1
मैंने कभी भी ऐसे कंपाइलर का इस्तेमाल नहीं किया है और मुझे यह सुनकर आश्चर्य नहीं होगा कि वे अब तक मर चुके हैं। और यहां तक ​​कि अगर वे नहीं है, एक गैर अनुरूप कार्यान्वयन के बारे में परवाह नहीं एक वैध निर्णय है। इसके बावजूद, दोनों libstdc ++ और libc ++ उपयोग static_assert(sizeof(T) > 0, "…");के अपने-अपने कार्यान्वयन में std::unique_ptrसुनिश्चित करने के लिए प्रकार है पूरा ...
5gon12eder

1
... तो मुझे लगता है कि यह कहना मुहावरा सुरक्षित है। वैसे भी, चूंकि sizeof(T)एक बूलियन संदर्भ में मूल्यांकन करना परीक्षण के बिल्कुल बराबर है sizeof(T) > 0, यह वास्तव में सौंदर्य कारणों के अलावा कोई फर्क नहीं पड़ता।
5gon12eder

3

शायद यह सुनिश्चित करने के लिए एक चाल Cपरिभाषित की गई है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.