2009-12-07 12 views
35
reinterpret_cast

मैं एक किताब पढ़ रहा हूँ और मैंने पाया कि reinterpret_cast सीधे नहीं किया जाना चाहिए, बल्कि static_cast के साथ संयोजन में शून्य करने के लिए * कास्टिंग का उपयोग कर:शून्य के माध्यम से कास्टिंग * बजाय

T1 * p1=... 
void *pv=p1; 
T2 * p2= static_cast<T2*>(pv); 
बजाय

:

T1 * p1=... 
T2 * p2= reinterpret_cast<T2*>(p1); 

हालांकि, मुझे कोई स्पष्टीकरण नहीं मिल रहा है कि यह प्रत्यक्ष कलाकारों से बेहतर क्यों है। अगर कोई मुझे स्पष्टीकरण दे या उत्तर में मुझे इंगित कर सकता है तो मैं बहुत सराहना करता हूं।

अग्रिम धन्यवाद

पेज। मैं जानता हूँ कि reinterpret_cast के लिए इस्तेमाल किया है क्या, लेकिन मैं कभी नहीं देखा कि इस तरह से

+2

यह पूछने के लिए धन्यवाद। ऐसा लगता है कि जवाब मानक में गहरा हो गया है। (इसे कई बार यूजनेट में पूछा, कोई भी इस बात की गारंटी नहीं दे सकता कि उस स्थिर-कास्ट अनुक्रम बेहतर क्या करता है)। वहां * कुछ * होना है: सी ++ 0x ने 'reinterpret_cast' के spec में कुछ शब्द जोड़ा, जो कि' static_cast' अनुक्रम 'के कुछ प्रकारों के साथ उपयोग किए जाने पर इसे फिर से लिखता है। –

+0

@sinec मुझे पूछना है कि आपको ऐसे कलाकारों को करने के लिए क्यों जरूरी लगता है? मैंने ऐसा करने की आवश्यकता के बिना सालों में सी ++ कोड के शेल्फ लोड लिखे हैं। –

+0

@ नील बटरवर्थ मैंने एक परियोजना पर काम किया (यह मौजूदा कोड में नई विशेषताएं जोड़ने जैसा था) और वहां बहुत सारे पागल जानवर थे, लेकिन यह अनिवार्य था क्योंकि हम विरासत कोड नहीं बदल सके। वैसे भी, मैंने इस सवाल से पूछा क्योंकि मैं एक पुस्तक पढ़ रहा हूं और मुझे इसके लिए कोई स्पष्टीकरण नहीं मिला। – sinek

उत्तर

24

में प्रयोग किया जाता है प्रकार कौन इस तरह के कलाकारों के लिए अनुमति दी है (उदाहरण के लिए यदि T1 एक पॉड प्रकार है और T2unsigned char है), static_cast साथ दृष्टिकोण है मानक द्वारा अच्छी तरह परिभाषित।

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

अधिक विशिष्ट होना करने के लिए, मैं सिर्फ मानक के प्रासंगिक भागों बोली होगा, महत्वपूर्ण भागों पर प्रकाश डाला:

5.2.10 [expr.reinterpret.cast]:

मानचित्रण द्वारा किया reinterpret_cast कार्यान्वयन-परिभाषित है। [नोट: यह मूल मान से अलग प्रतिनिधित्व प्रस्तुत कर सकता है, या नहीं हो सकता है।] ... किसी ऑब्जेक्ट के लिए पॉइंटर को अलग-अलग प्रकार के ऑब्जेक्ट में पॉइंटर में स्पष्ट रूप से परिवर्तित किया जा सकता है।) सिवाय इसके कि किसी प्रकार के रावल्यू को परिवर्तित करना "टी 1 के लिए पॉइंटर" प्रकार "पॉइंटर टू टी 2" (जहां टी 1 और टी 2 ऑब्जेक्ट प्रकार हैं और जहां टी 2 की संरेखण आवश्यकताएं टी 1 की तुलना में कोई कठोर नहीं हैं) और इसके मूल प्रकार पर मूल पॉइंटर मान, उत्पन्न होता है ऐसे सूचक रूपांतरण का नतीजा निर्दिष्ट नहीं है।

तो कुछ इस तरह:

struct pod_t { int x; }; 
pod_t pod; 
char* p = reinterpret_cast<char*>(&pod); 
memset(p, 0, sizeof pod); 

प्रभावी रूप से अनिर्दिष्ट है।

समझाते हुए क्यों static_cast काम थोड़ा और मुश्किल है। यहाँ ऊपर कोड static_cast जो मेरा मानना ​​है कि हमेशा की तरह स्टैंडर्ड द्वारा इच्छित कार्य करने की गारंटी है उपयोग करने के लिए फिर से लिखा है: एक बार फिर

struct pod_t { int x; }; 
pod_t pod; 
char* p = static_cast<char*>(static_cast<void*>(&pod)); 
memset(p, 0, sizeof pod); 

, मुझे यह निष्कर्ष निकला कि ऊपर स्टैंडर्ड के वर्गों है कि, एक साथ, मुझे नेतृत्व बोली जाने पोर्टेबल होना चाहिए:

3.9 [मूलभूत।प्रकार]:

पॉड प्रकार टी के किसी भी वस्तु (एक आधार स्तरीय subobject के अलावा अन्य) के लिए, या नहीं, ऑब्जेक्ट प्रकार टी की कोई मान्य मान रखती है, अंतर्निहित बाइट्स (1.7) वस्तु बनाने कर सकते हैं चार या हस्ताक्षरित चार की सरणी में कॉपी किया जाना चाहिए। यदि चार या हस्ताक्षरित चार की सरणी की सामग्री ऑब्जेक्ट में वापस कॉपी की जाती है, तो ऑब्जेक्ट इसके बाद उसका मूल मान रखेगा।

टाइप टी के किसी ऑब्जेक्ट का ऑब्जेक्ट प्रस्तुति एन टी हस्ताक्षर किए गए चार ऑब्जेक्ट्स के प्रकार टी द्वारा ऑब्जेक्ट किया गया है, जहां एन आकार (टी) के बराबर है।

3.9.2 [basic.compound]:

सीवी-योग्य (3.9.3) या सीवी-अयोग्य प्रकार void* (शून्य करने के लिए सूचक) की वस्तुओं, वस्तुओं को इंगित करने के लिए इस्तेमाल किया जा सकता है अज्ञात प्रकार का। एक void* कोई ऑब्जेक्ट पॉइंटर पकड़ने में सक्षम होगा। एक सीवी-योग्य या सीवी-अयोग्य (3.9.3) void* में सीवी-योग्यता या सीवी-अयोग्य char* के समान प्रतिनिधित्व और संरेखण आवश्यकताएं होंगी।

3,10 [basic.lval]:

एक कार्यक्रम निम्नलिखित प्रकारों में से एक के अलावा अन्य के lvalue के माध्यम से एक वस्तु की संग्रहीत मूल्य तक पहुँचने के लिए प्रयास करता है व्यवहार अपरिभाषित है):

  • ...
  • एक चार या अहस्ताक्षरित चार प्रकार

4,10 [conv.ptr]:

प्रकार की जहां टी एक वस्तु प्रकार, "करने के लिए प्रकार का एक rvalue में बदला जा सकता सूचक है" सीवी टी, सूचक "एक rvalue सीवी शून्य। "एक" पॉइंटर से सीवी टी "को" पॉइंटर से सीवी शून्य "में परिवर्तित करने का नतीजा भंडारण स्थान की शुरुआत के लिए इंगित करता है जहां टाइप टी का ऑब्जेक्ट रहता है, जैसे कि ऑब्जेक्ट सबसे व्युत्पन्न वस्तु है (1.8) प्रकार टी (यानी, बेस क्लास सबोबजेक्ट नहीं है)।

5.2.9 [expr.static.cast]:

किसी भी मानक रूपांतरण अनुक्रम (खंड 4), lvalue करने वाली rvalue (4.1), सरणी-topointer के अलावा अन्य का प्रतिलोम (4.2), फ़ंक्शन-टू-पॉइंटर (4.3), और बूलियन (4.12) रूपांतरण, static_cast का उपयोग करके स्पष्ट रूप से निष्पादित किया जा सकता है।

[संपादित करें] दूसरी ओर, हम इस मणि है:

9.2 [class.mem]/17:

एक पॉड-struct वस्तु के लिए एक सूचक, उपयुक्त रूप से reinterpret_cast का उपयोग करके परिवर्तित किया गया है, इसके प्रारंभिक सदस्य को इंगित करता है (या यदि वह सदस्य थोड़ा-फ़ील्ड है, फिर उस यूनिट में जिसमें वह रहता है) और इसके विपरीत।[नोट: हो सकता है इसलिए एक पीओडी-स्ट्रक्चर ऑब्जेक्ट के भीतर अज्ञात पैडिंग हो, लेकिन इसकी शुरुआत में उचित संरेखण प्राप्त करने के लिए आवश्यक नहीं है। ]

जो इंगित करता है कि reinterpret_cast पॉइंटर्स के बीच किसी भी तरह से "एक ही पता" का तात्पर्य है। जाओ पता लगाओ।

+1

लेकिन 'static_cast' के परिणामस्वरूप,' void * 'के रूपांतरण के बारे में और * * अलग * प्रकार पर वापस जाने की कोई गारंटी नहीं है। यह सिर्फ इतना कहता है "टाइप पॉइंटर का मूल्य" पॉइंटर से सीवी शून्य "में परिवर्तित करने के लिए और मूल सूचक प्रकार पर वापस मूल मूल्य होगा।" –

+0

संपादित उत्तर देखें। इसमें कोई स्पष्ट बयान नहीं है कि "यह ठीक है", लेकिन मानक में कई संदर्भ हैं जो दृढ़ता से ऐसा मानते हैं। विशेष रूप से ध्यान दें कि किसी भी पीओडी में char _objects_ होते हैं, और 'शून्य *' के लिए एक पीओडी संरचना सूचक को static_cast'ing 'शून्य * 'सूचक बनाता है जो पहले ऐसे चार ऑब्जेक्ट को इंगित करता है (क्योंकि पीओडी structs में कोई प्रारंभिक पैडिंग की अनुमति नहीं है)। –

+0

आईई।यदि हम किसी भी तरह से ऑब्जेक्ट के प्रतिनिधित्व के पहले चार को पॉइंटर प्राप्त करते हैं (static_cast के बिना) और उसे 'शून्य *' पर डालें, तो मानक अप्रत्यक्ष रूप से इसे पॉइंटर के बराबर होना चाहिए ताकि ऑब्जेक्ट को 'शून्य *' पर डाला जा सके। इसके बाद से, जैसा कि हम पूर्व सूचक को 'char *' पर वापस ला सकते हैं और इसे काम कर सकते हैं, इसलिए हम बाद वाले पॉइंटर को 'char *' पर "बैक-कास्ट" कर सकते हैं और इसे भी काम कर सकते हैं (जैसा कि यह है एक ही सूचक मूल्य!)। –

3

इसका कारण यह है कि सी ++ विरासत को परिभाषित करता है, और सदस्य पॉइंटर्स की वजह से।

सी के साथ, सूचक बहुत ही एक पता है, जैसा कि होना चाहिए। सी ++ में इसकी कुछ विशेषताओं के कारण इसे और अधिक जटिल होना चाहिए।

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

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

वास्तव में उम्मीद है कि आप इन मामलों का पहले स्थान पर कभी भी उपयोग नहीं करेंगे। इसके अलावा, अगर आप बहुत कुछ कास्टिंग कर रहे हैं तो यह एक और संकेत है कि आप अपने डिजाइन में गड़बड़ कर रहे हैं।

एकमात्र बार जब मैं कास्टिंग समाप्त करता हूं तो क्षेत्रों में प्राइमेटिव के साथ होता है सी ++ निर्णय समान नहीं होते हैं, लेकिन स्पष्ट रूप से उन्हें होना चाहिए। वास्तविक वस्तुओं के लिए, जब भी आप कुछ डालना चाहते हैं, तो अपने डिजाइन पर सवाल उठाना शुरू करें क्योंकि आपको अधिकांश समय 'इंटरफ़ेस में प्रोग्रामिंग' होना चाहिए। बेशक, आप नहीं बदल सकते कि कैसे तृतीय पक्ष एपीआई काम करते हैं ताकि आपके पास हमेशा अधिक पसंद न हो।

+0

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

6

इस बात का मामूली संदेह नहीं है कि दोनों रूपों को अच्छी तरह से परिभाषित किया गया है, लेकिन शब्द इसे पकड़ने में विफल रहता है।

दोनों रूप अभ्यास में काम करेंगे।

reinterpret_cast इरादे के बारे में अधिक स्पष्ट है और इसे प्राथमिकता दी जानी चाहिए।

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