Static_cast, डायनेमिक_कास्ट, const_cast और reinterpret_cast का उपयोग कब किया जाना चाहिए?


2490

के उचित उपयोग क्या हैं:

  • static_cast
  • dynamic_cast
  • const_cast
  • reinterpret_cast
  • सी-स्टाइल कास्ट (type)value
  • फंक्शन-स्टाइल कास्ट type(value)

कोई कैसे तय करता है कि किन विशिष्ट मामलों में उपयोग करना है?



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

2
आप अपने प्रश्न के लिए वास्तव में अच्छे उत्तर पा सकते हैं। लेकिन मैं यहाँ एक और बात रखना चाहूंगा, @ e.James "ऐसा कुछ भी नहीं है जो ये नए c ++ कास्ट ऑपरेटर कर सकते हैं और c स्टाइल कास्ट नहीं कर सकते। ये बेहतर कोड पठनीयता के लिए कम या ज्यादा जोड़े जाते हैं।"
ब्रेकबेडएसपी

@BreakBadSP नई जातियाँ केवल बेहतर कोड पठनीयता के लिए हैं। वे खतरनाक चीजों को करने के लिए इसे कठिन बनाने के लिए वहां हैं, जैसे कि उनके मूल्यों के बजाय कास्ट या कास्टिंग पॉइंट्स को दूर करना। static_cast में एसी स्टाइल कास्ट की तुलना में कुछ खतरनाक करने की बहुत कम संभावनाएं हैं!
चौदह

@FourtyTwo ने सहमति व्यक्त की
ब्रेकबेडएसपी

जवाबों:


2569

static_castपहली कास्ट है जिसे आपको उपयोग करने का प्रयास करना चाहिए। यह (जैसे प्रकार के बीच निहित रूपांतरणों को करता है intकरने के लिए float, या सूचक को void*), और यह भी स्पष्ट रूपांतरण कार्यों (या छुपा हुआ वाले) कह सकते हैं। कई मामलों में, स्पष्ट रूप से बताते हुए static_castआवश्यक नहीं है, लेकिन यह ध्यान रखना महत्वपूर्ण है कि T(something)वाक्यविन्यास के बराबर है (T)somethingऔर इससे बचा जाना चाहिए (बाद में उस पर अधिक)। एक T(something, something_else), सुरक्षित है तथापि, और निर्माता कॉल करने के लिए गारंटी।

static_castवंशानुगत पदानुक्रम के माध्यम से भी डाली जा सकती है। ऊपर की ओर (बेस क्लास की ओर) कास्टिंग करते समय यह अनावश्यक है, लेकिन जब नीचे की ओर कास्टिंग की जाती है तो इसका उपयोग तब तक किया जा सकता है जब तक यह virtualविरासत के माध्यम से नहीं डाली जाती है । हालाँकि, यह जाँच नहीं करता है, और यह static_castएक प्रकार के पदानुक्रम को नीचे करने के लिए अपरिभाषित व्यवहार है जो वास्तव में ऑब्जेक्ट का प्रकार नहीं है।


const_castconstएक चर को हटाने या जोड़ने के लिए इस्तेमाल किया जा सकता है ; कोई अन्य C ++ कास्ट इसे हटाने में सक्षम है (नहीं भी reinterpret_cast)। यह ध्यान रखना महत्वपूर्ण है कि constमूल चर होने पर पूर्व मान को संशोधित करना केवल अपरिभाषित है const; यदि आप इसका उपयोग constकिसी ऐसी चीज़ का संदर्भ लेने के लिए करते हैं const, जिसके साथ घोषित नहीं किया गया था , तो यह सुरक्षित है। यह उपयोगी हो सकता है जब constउदाहरण के लिए, के आधार पर सदस्य कार्यों को ओवरलोड करना । इसका उपयोग constकिसी ऑब्जेक्ट को जोड़ने के लिए भी किया जा सकता है , जैसे कि एक सदस्य फ़ंक्शन को अधिभार देना।

const_castयह भी इसी तरह से काम करता है volatile, हालांकि यह कम आम है।


dynamic_castबहुरूपता से निपटने के लिए विशेष रूप से उपयोग किया जाता है। आप किसी पॉलीमॉर्फ़िक प्रकार को किसी अन्य वर्ग प्रकार के लिए एक पॉइंटर या संदर्भ दे सकते हैं (एक पॉलीमॉर्फिक प्रकार में कम से कम एक आभासी फ़ंक्शन है, घोषित या विरासत में मिला है)। आप इसे केवल नीचे की ओर कास्टिंग से अधिक के लिए उपयोग कर सकते हैं - आप बग़ल में या यहां तक ​​कि दूसरी श्रृंखला भी डाल सकते हैं। dynamic_castवांछित वस्तु की तलाश और इसे वापस यदि संभव हो जाएगा। यदि ऐसा नहीं हो सकता है, तो यह nullptrएक पॉइंटर के मामले में वापस आ जाएगा , या std::bad_castएक संदर्भ के मामले में फेंक देगा ।

dynamic_castहालांकि कुछ सीमाएं हैं। यदि वंशानुक्रम पदानुक्रम (तथाकथित 'खूंखार हीरा') में एक ही प्रकार की कई वस्तुएं हैं तो यह काम नहीं करता है और आप virtualवंशानुक्रम का उपयोग नहीं कर रहे हैं । यह केवल सार्वजनिक विरासत के माध्यम से भी जा सकता है - यह हमेशा protectedया privateविरासत के माध्यम से यात्रा करने में विफल रहेगा । यह शायद ही कभी एक मुद्दा है, हालांकि, विरासत के ऐसे रूप दुर्लभ हैं।


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


सी-शैली डाली और समारोह शैली डाली का उपयोग कर डाले हैं (type)objectया type(object), क्रमशः, और कार्यात्मक रूप से बराबर कर रहे हैं। उन्हें निम्नलिखित में से पहले के रूप में परिभाषित किया गया है जो सफल होता है:

  • const_cast
  • static_cast (हालांकि उपयोग प्रतिबंधों की अनदेखी)
  • static_cast (ऊपर देखें), फिर const_cast
  • reinterpret_cast
  • reinterpret_cast, फिर const_cast

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

सी-स्टाइल कास्ट्स भी प्रदर्शन करते समय अभिगम नियंत्रण की उपेक्षा करते हैं static_cast, जिसका अर्थ है कि उनके पास एक ऑपरेशन करने की क्षमता है जो कोई अन्य कलाकार नहीं कर सकता है। यह ज्यादातर एक कीचड़ है, हालांकि, और मेरे दिमाग में सी-स्टाइल कास्ट से बचने का सिर्फ एक और कारण है।


17
डायनामिक_कास्ट केवल बहुरूपी प्रकारों के लिए है। आपको केवल इसका उपयोग करने की आवश्यकता है जब आप एक व्युत्पन्न वर्ग के लिए कास्टिंग कर रहे हों। static_cast निश्चित रूप से पहला विकल्प है जब तक कि आपको विशेष रूप से डायनेमिक_कास्ट के functionalinoality की आवश्यकता नहीं है। यह सामान्य रूप से कुछ चमत्कारी रजत-गोली "टाइप-चेकिंग कास्ट" नहीं है।
११:२०

2
बहुत बढ़िया जवाब! एक त्वरित टिप्पणी: जब आप एक व्युत्पन्न * और बेस * में डालना चाहते हैं, तो स्थैतिक_कास्ट आवश्यक हो सकता है, क्योंकि डबल पॉइंटर्स / संदर्भ स्वचालित रूप से पदानुक्रम को नहीं डालते हैं। मैं दो मिनट पहले ऐसी (स्पष्ट रूप से, सामान्य नहीं) स्थिति में आया था। ;-)
bartgol

5
* "कोई अन्य C ++ कास्ट हटाने में सक्षम है const(नहीं भी reinterpret_cast)" ... वास्तव में? किस बारे में reinterpret_cast<int *>(reinterpret_cast<uintptr_t>(static_cast<int const *>(0)))?
user541686

29
मुझे लगता है कि ऊपर गायब एक महत्वपूर्ण विवरण यह है कि डायनेमिक_कास्ट में स्थैतिक या रीइंटरप्रिट_कास्ट की तुलना में रन-टाइम परफॉर्मेंस पेनल्टी है। यह महत्वपूर्ण है, उदाहरण के लिए वास्तविक समय सॉफ्टवेयर में।
jfritz42

5
यह उल्लेख के लायक हो सकता है कि reinterpret_castअक्सर एपीआई का सेट अपारदर्शी डेटा प्रकारों के साथ काम करते समय पसंद का हथियार होता है
कक्षा कंकाल

333

dynamic_castएक विरासत पदानुक्रम के भीतर संकेत / संदर्भ परिवर्तित करने के लिए उपयोग करें ।

static_castसाधारण प्रकार के रूपांतरणों के लिए उपयोग करें ।

reinterpret_castबिट पैटर्न के निम्न-स्तरीय पुनर्व्याख्या के लिए उपयोग करें । अत्यधिक सावधानी के साथ उपयोग करें।

const_castदूर कास्टिंग के लिए उपयोग करें const/volatile। इससे बचें जब तक कि आप एक कास्ट-गलत एपीआई का उपयोग करके अटक नहीं जाते हैं।


2
डायनामिक_कास्ट से सावधान रहें। यह RTTI पर निर्भर करता है और यह साझा पुस्तकालयों की सीमाओं के अनुसार अपेक्षित रूप से काम नहीं करेगा। केवल इसलिए कि आप निष्पादन योग्य और साझा पुस्तकालय का स्वतंत्र रूप से निर्माण करते हैं, विभिन्न बिल्डरों में आरटीटीआई को सिंक करने का कोई मानकीकृत तरीका नहीं है। इस कारण से Qt लाइब्रेरी में qobject_cast मौजूद है> <जो जाँच प्रकारों के लिए QObject प्रकार की जानकारी का उपयोग करता है।
user3150128

198

(ऊपर बहुत सैद्धांतिक और वैचारिक व्याख्या दी गई है)

नीचे में से कुछ हैं व्यावहारिक उदाहरण है जब मैं इस्तेमाल किया static_cast , dynamic_cast , const_cast , reinterpret_cast

(स्पष्टीकरण को समझने के लिए इसे भी संदर्भित करता है: http://www.cplusplus.com/doc/tutorial/typecasting/ )

static_cast:

OnEventData(void* pData)

{
  ......

  //  pData is a void* pData, 

  //  EventData is a structure e.g. 
  //  typedef struct _EventData {
  //  std::string id;
  //  std:: string remote_id;
  //  } EventData;

  // On Some Situation a void pointer *pData
  // has been static_casted as 
  // EventData* pointer 

  EventData *evtdata = static_cast<EventData*>(pData);
  .....
}

डायनामिक_कास्ट:

void DebugLog::OnMessage(Message *msg)
{
    static DebugMsgData *debug;
    static XYZMsgData *xyz;

    if(debug = dynamic_cast<DebugMsgData*>(msg->pdata)){
        // debug message
    }
    else if(xyz = dynamic_cast<XYZMsgData*>(msg->pdata)){
        // xyz message
    }
    else/* if( ... )*/{
        // ...
    }
}

const_cast:

// *Passwd declared as a const

const unsigned char *Passwd


// on some situation it require to remove its constness

const_cast<unsigned char*>(Passwd)

reinterpret_cast:

typedef unsigned short uint16;

// Read Bytes returns that 2 bytes got read. 

bool ByteBuffer::ReadUInt16(uint16& val) {
  return ReadBytes(reinterpret_cast<char*>(&val), 2);
}

31
अन्य उत्तरों में से कुछ के सिद्धांत अच्छे हैं, लेकिन फिर भी भ्रमित हैं, इन उदाहरणों को अन्य उत्तरों को पढ़ने के बाद देखने से वास्तव में उन सभी को समझ में आता है। उदाहरण के बिना, मैं अभी भी अनिश्चित था, लेकिन उनके साथ, मुझे अब यकीन है कि अन्य उत्तरों का क्या मतलब है।
सोलक्स

1
Reinterpret_cast के अंतिम उपयोग के बारे में: क्या यह उपयोग करने के समान नहीं है static_cast<char*>(&val)?
लोरेंजो बेल्ली

3
@LorenzoBelli बिल्कुल नहीं। क्या तुमने कोशिश की? उत्तरार्द्ध मान्य नहीं है C ++ और संकलन संकलन। static_castकेवल परिभाषित रूपांतरणों के साथ प्रकारों के बीच काम करता है, वंशानुक्रम द्वारा दृश्य संबंध या / से void *। बाकी सभी चीजों के लिए, अन्य जातियां हैं। reinterpret castकिसी भी char *प्रकार के किसी भी ऑब्जेक्ट के प्रतिनिधित्व को पढ़ने की अनुमति देने की अनुमति है - और केवल उन मामलों में से एक जहां कीवर्ड उपयोगी है, न कि कार्यान्वयन के एक बड़े पैमाने पर जनरेटर- / अपरिभाषित व्यवहार। लेकिन इसे 'सामान्य' रूपांतरण नहीं माना जाता है, इसलिए (आमतौर पर) बहुत रूढ़िवादी द्वारा इसकी अनुमति नहीं दी जाती है static_cast
अंडरस्कोर_ड

2
जब आप डेटाबेस जैसे सिस्टम सॉफ़्टवेयर के साथ काम कर रहे हों, तो reinterpret_cast बहुत आम है। अधिकांश मामले आप अपने स्वयं के पेज मैनेजर को लिखते हैं, जिसके बारे में कोई पता नहीं होता है कि पेज में संग्रहीत डेटा प्रकार क्या है और केवल एक शून्य पॉइंटर लौटाता है। इसकी उच्च स्तर तक एक पुनर्व्याख्या कास्ट करने के लिए और इसे जो कुछ भी चाहते हैं, उसका अनुमान लगाने के लिए।
सोहैब

1
Const_cast उदाहरण अपरिभाषित व्यवहार प्रदर्शित करता है। Const के रूप में घोषित एक चर de-const-ed नहीं हो सकता है। हालाँकि, एक नॉन-कास्ट के रूप में घोषित किया गया एक वैरिएबल जो किसी फ़ंक्शन को एक कॉस्ट रेफरेंस में ले जाता है, उस फंक्शन में डे-कॉन्स्ट-एड हो सकता है, जिसमें वह यूबी होता है।
जोहान गेर्ले

99

यदि आप बहुत कम इंटर्ल्स जानते हैं तो यह मदद कर सकता है ...

static_cast

  • C ++ कंपाइलर पहले से ही जानता है कि स्केलर प्रकारों के बीच कैसे बदलना है जैसे कि फ्लोट टू इंट। static_castउनके लिए उपयोग करें।
  • जब आप कंपाइलर को टाइप से कन्वर्ट करने के Aलिए कहते हैं B, तो कंस्ट्रक्टर को पारम कहते हैं। वैकल्पिक रूप से, एक रूपांतरण ऑपरेटर (यानी ) हो सकता है । यदि इस तरह का कोई निर्माणकर्ता नहीं है, या आपके पास रूपांतरण ऑपरेटर नहीं है, तो आपको संकलन समय त्रुटि मिलती है।static_castBAAA::operator B()BA
  • से कास्ट A*करने के लिए B*हमेशा सफल होता है ए और बी वंशानुगत पदानुक्रम (या शून्य) में हैं अन्यथा आप संकलन त्रुटि मिलती है।
  • गोटचा : यदि आप बेस पॉइंटर को व्युत्पन्न पॉइंटर में डालते हैं लेकिन यदि वास्तविक ऑब्जेक्ट वास्तव में व्युत्पन्न प्रकार नहीं है, तो आपको त्रुटि नहीं मिलती है। आपको खराब पॉइंटर मिलता है और रनटाइम में बहुत सीगफॉल्ट की संभावना होती है। उसी के लिए जाता A&है B&
  • Gotcha : Derived से बेस या वाइसवर्सा से कास्ट नई कॉपी बनाता है! C # / Java से आने वाले लोगों के लिए, यह एक बहुत बड़ा आश्चर्य हो सकता है क्योंकि परिणाम मूल रूप से व्युत्पन्न से बनाई गई एक कटी हुई वस्तु है।

dynamic_cast

  • डायनेमिक_कास्ट रनटाइम प्रकार की जानकारी का उपयोग करके यह पता लगाने के लिए करता है कि क्या कास्ट वैध है। उदाहरण के लिए, (Base*)करने के लिए (Derived*)विफल हो सकता है अगर सूचक व्युत्पन्न प्रकार की वास्तव में नहीं है।
  • इसका मतलब है, static_cast की तुलना में डायनेमिक_कास्ट बहुत महंगा है!
  • के A*लिए B*, यदि कास्ट अमान्य है, तो डायनेमिक_कास्ट nullptr वापस आ जाएगा।
  • के लिए A&करने के लिए B&करता है, तो डाली अमान्य है तो dynamic_cast bad_cast अपवाद फेंक देते हैं।
  • अन्य जातियों के विपरीत, रनटाइम ओवरहेड है।

const_cast

  • जबकि static_cast नॉन-कॉन्स्ट को कॉन्स्ट कर सकता है, यह दूसरे रास्ते पर नहीं जा सकता। Const_cast दोनों तरीके से कर सकता है।
  • एक उदाहरण जहां यह काम आता है, कुछ कंटेनर के माध्यम से पुनरावृत्ति होती है जैसे set<T>कि केवल अपने तत्वों को कॉन्स्टेंट के रूप में देता है ताकि यह सुनिश्चित हो सके कि आप इसकी कुंजी को नहीं बदलते हैं। हालाँकि यदि आपका इरादा ऑब्जेक्ट के गैर-प्रमुख सदस्यों को संशोधित करना है तो यह ठीक होना चाहिए। आप कब्ज को दूर करने के लिए const_cast का उपयोग कर सकते हैं।
  • एक और उदाहरण है जब आप और T& SomeClass::foo()साथ ही लागू करना चाहते हैं const T& SomeClass::foo() const। कोड दोहराव से बचने के लिए, आप दूसरे से एक फ़ंक्शन का मान वापस करने के लिए const_cast लागू कर सकते हैं।

reinterpret_cast

  • यह मूल रूप से कहता है कि इस मेमोरी स्थान पर इन बाइट्स को लें और इसे दी गई वस्तु के रूप में सोचें।
  • उदाहरण के लिए, आप फ्लोट के 4 बाइट्स को 4 बाइट्स के उदाहरण के लिए लोड कर सकते हैं, यह देखने के लिए कि फ्लोट में बिट्स कैसा दिखता है।
  • जाहिर है, अगर डेटा टाइप के लिए सही नहीं है, तो आपको सेगफॉल्ट हो सकता है।
  • इस कास्ट के लिए कोई रनटाइम ओवरहेड नहीं है।

मैंने रूपांतरण ऑपरेटर की जानकारी जोड़ी है, लेकिन कुछ अन्य चीजें भी हैं जिन्हें ठीक किया जाना चाहिए और मुझे यह महसूस नहीं होता है कि यह बहुत ज्यादा अद्यतन है। आइटम हैं: 1. If you cast base pointer to derived pointer but if actual object is not really derived type then you don't get error. You get bad pointer and segfault at runtime.आप यूबी प्राप्त करते हैं जिसके परिणामस्वरूप यदि आप भाग्यशाली हैं तो रनगेट पर सेगफॉल्ट हो सकता है। 2. डायनामिक कास्ट का उपयोग क्रॉस कास्टिंग में भी किया जा सकता है। 3. कास्ट कास्ट कुछ मामलों में यूबी में परिणाम कर सकते हैं। mutableतार्किक विवशता को लागू करने के लिए उपयोग करना एक बेहतर विकल्प हो सकता है।
एड्रियन

1
@ एड्रियन आप सभी गिनती में सही हैं। उत्तर को कम या ज्यादा शुरुआती स्तर पर लोगों के लिए लिखा गया है और मैं उन्हें अन्य सभी जटिलताओं के साथ उन पर हावी नहीं होना चाहता था mutable, जो कास्टिंग के साथ आते हैं आदि
शीतल शाह

16

क्या यह आपके प्रश्न का उत्तर देता है?

मैंने कभी उपयोग नहीं किया है reinterpret_cast, और आश्चर्य है कि क्या ऐसे मामले में चल रहा है जिसे इसकी आवश्यकता नहीं है, यह खराब डिजाइन की गंध नहीं है। मेरे द्वारा काम किए जाने वाले कोड आधार में dynamic_castबहुत उपयोग किया जाता है। इसके साथ अंतर static_castयह है कि एक dynamic_castरनटाइम चेकिंग होती है जो (सुरक्षित) या (अधिक ओवरहेड) हो सकती है जो आप चाहते हैं (देखें msdn )।


3
मैंने एक उद्देश्य के लिए reintrepret_cast का उपयोग किया है - बिट्स को एक डबल (मेरे प्लेटफ़ॉर्म पर लंबे समय तक समान आकार) से बाहर निकालना।
जोशुआ

2
COM ऑब्जेक्ट्स के साथ काम करने के लिए reinterpret_cast की आवश्यकता है। CoCreateInstance () में प्रकार शून्य (अंतिम पैरामीटर) का आउटपुट पैरामीटर है, जिसमें आप अपने पॉइंटर को "INetFwPolicy2 * pNetFwPolicy2" जैसे घोषित करेंगे। ऐसा करने के लिए, आपको reinterpret_cast <void **> (& pNetFwPolicy2) जैसा कुछ लिखना होगा।
सर्ज रोजैच

1
शायद एक अलग दृष्टिकोण है, लेकिन मैं reinterpret_castएक सरणी से डेटा के टुकड़े निकालने के लिए उपयोग करता हूं । उदाहरण के लिए अगर मेरे पास एक char*बफ़र है जिसमें पैक्ड बाइनरी डेटा भरा हुआ है, जिसे मुझे अलग-अलग प्रकार के अलग-अलग प्राथमिकताओं के माध्यम से स्थानांतरित करने और प्राप्त करने की आवश्यकता है। कुछ इस तरह:template<class ValType> unsigned int readValFromAddress(char* addr, ValType& val) { /*On platforms other than x86(_64) this could do unaligned reads, which could be bad*/ val = (*(reinterpret_cast<ValType*>(addr))); return sizeof(ValType); }
जेम्स मटका

मैंने कभी उपयोग नहीं किया reinterpret_cast, इसके लिए बहुत सारे उपयोग नहीं हैं।
पिका द विजार्ड ऑफ द व्हेल्स

व्यक्तिगत रूप से मैंने केवल reinterpret_castएक कारण के लिए कभी देखा है। मैंने कच्चे ऑब्जेक्ट डेटा को डेटाबेस में "ब्लॉब" डेटाटाइप पर संग्रहीत किया है, तब जब डेटा डेटाबेस से पुनर्प्राप्त reinterpret_castकिया जाता है , तो इस कच्चे डेटा को ऑब्जेक्ट में बदलने के लिए उपयोग किया जाता है।
काल्पनिक

15

अब तक के अन्य उत्तरों के अलावा, यहां स्पष्ट उदाहरण static_castहै कि पर्याप्त नहीं है जहां reinterpret_castइसकी आवश्यकता है। मान लीजिए कि एक फ़ंक्शन है जो आउटपुट पैरामीटर में विभिन्न वर्गों की वस्तुओं (जो एक सामान्य आधार वर्ग को साझा नहीं करता है) की ओर संकेत देता है। ऐसे फ़ंक्शन का एक वास्तविक उदाहरण है CoCreateInstance()(अंतिम पैरामीटर देखें, जो वास्तव में है void**)। मान लें कि आप इस फ़ंक्शन से ऑब्जेक्ट के विशेष वर्ग का अनुरोध करते हैं, तो आप पहले से ही सूचक के लिए प्रकार जानते हैं (जो आप अक्सर कॉम ऑब्जेक्ट्स के लिए करते हैं)। इस मामले में आप में अपने सूचक को सूचक डाली नहीं कर सकते हैं void**के साथ static_castआप की जरूरत है: reinterpret_cast<void**>(&yourPointer)

कोड में:

#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
    CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
    //static_cast<void**>(&pNetFwPolicy2) would give a compile error
    reinterpret_cast<void**>(&pNetFwPolicy2) );

हालाँकि, static_castसरल पॉइंटर्स (पॉइंटर्स टू पॉइंटर्स) के लिए काम करता है, इसलिए उपरोक्त कोड reinterpret_castको निम्नलिखित तरीके से (अतिरिक्त चर की कीमत पर) से बचने के लिए फिर से लिखा जा सकता है :

#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
void* tmp = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
    CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
    &tmp );
pNetFwPolicy2 = static_cast<INetFwPolicy2*>(tmp);

क्या &static_cast<void*>(pNetFwPolicy2)इसके बजाय यह कुछ काम नहीं करेगा static_cast<void**>(&pNetFwPolicy2)?
jp48

9

हालांकि अन्य जवाबों ने सी ++ जातियों के बीच सभी अंतरों का अच्छी तरह से वर्णन किया है, मैं एक लघु नोट जोड़ना चाहूंगा कि आपको सी-स्टाइल कलाकारों (Type) varऔर का उपयोग क्यों नहीं करना चाहिए Type(var)

C ++ शुरुआती के लिए C- शैली की जातियां C ++ जातियों (static_cast <>), डायनामिक_कास्ट <> (), const_cast <> (), reinterpret_cast <> () पर सुपरसेट ऑपरेशन की तरह दिखती हैं और कोई भी व्यक्ति उन्हें C ++ जातियों के ऊपर पसंद कर सकता है। । वास्तव में सी-स्टाइल कास्ट सुपरसेट और लिखने के लिए छोटा है।

सी-शैली के कलाकारों की मुख्य समस्या यह है कि वे डेवलपर को कलाकारों के वास्तविक इरादे को छिपाते हैं। C- शैली की जातियां लगभग सभी प्रकार की कास्टिंग कर सकती हैं, जो आमतौर पर static_cast <> () और डायनामिक_कास्ट <() द्वारा की जाती हैं जैसे कि const_cast <> () के लिए संभावित खतरनाक जातियाँ, जहाँ const modifier को हटाया जा सकता है ताकि const चर संशोधित किया जा सकता है और reinterpret_cast <> () भी संकेत करने के लिए पूर्णांक मान पुन: व्याख्या कर सकते हैं।

यहाँ नमूना है।

int a=rand(); // Random number.

int* pa1=reinterpret_cast<int*>(a); // OK. Here developer clearly expressed he wanted to do this potentially dangerous operation.

int* pa2=static_cast<int*>(a); // Compiler error.
int* pa3=dynamic_cast<int*>(a); // Compiler error.

int* pa4=(int*) a; // OK. C-style cast can do such cast. The question is if it was intentional or developer just did some typo.

*pa4=5; // Program crashes.

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

यहाँ Bjarne Stroustrup (C ++ के लेखक) की पुस्तक C ++ प्रोग्रामिंग लैंग्वेज 4th संस्करण का एक छोटा उद्धरण है - पृष्ठ 302।

यह सी-स्टाइल कास्ट नामित रूपांतरण ऑपरेटरों की तुलना में कहीं अधिक खतरनाक है क्योंकि संकेतन एक बड़े कार्यक्रम में स्पॉट करने के लिए कठिन है और प्रोग्रामर द्वारा जिस तरह का रूपांतरण करना है वह स्पष्ट नहीं है।


5

समझने के लिए, आइए नीचे कोड स्निपेट पर विचार करें:

struct Foo{};
struct Bar{};

int main(int argc, char** argv)
{
    Foo* f = new Foo;

    Bar* b1 = f;                              // (1)
    Bar* b2 = static_cast<Bar*>(f);           // (2)
    Bar* b3 = dynamic_cast<Bar*>(f);          // (3)
    Bar* b4 = reinterpret_cast<Bar*>(f);      // (4)
    Bar* b5 = const_cast<Bar*>(f);            // (5)

    return 0;
}

केवल पंक्ति (4) त्रुटि के बिना संकलित होती है। केवल रीइंटरटेनमेंट_कास्ट का उपयोग पॉइंटर को ऑब्जेक्ट से पॉइंटर को किसी असंबंधित ऑब्जेक्ट प्रकार में बदलने के लिए किया जा सकता है।

इस पर ध्यान दिया जाना है: डायनामिक_कास्ट रन-टाइम में विफल हो जाएगा, हालांकि अधिकांश कंपाइलरों पर यह संकलित करने में भी विफल हो जाएगा क्योंकि पॉइंटर को डाले जाने की संरचना में कोई वर्चुअल फ़ंक्शन नहीं हैं, जिसका अर्थ है कि डायनेमिक_कास्ट केवल पॉलीफ़ोरफ़िक क्लास पॉइंटर्स के साथ काम करेगा ।

C ++ कास्ट का उपयोग कब करें :

  • Static_cast का उपयोग सी-स्टाइल कास्ट के बराबर के रूप में करें जो मूल्य रूपांतरण करता है, या जब हमें कक्षा से इसके सुपरक्लास को स्पष्ट रूप से अप-कास्ट करने की आवश्यकता होती है।
  • कांस्टेबल को हटाने के लिए const_cast का प्रयोग करें ।
  • पूर्णांक और अन्य सूचक प्रकारों के लिए सूचक प्रकारों के असुरक्षित रूपांतरण करने के लिए reinterpret_cast का उपयोग करें । इसका उपयोग केवल अगर हम जानते हैं कि हम क्या कर रहे हैं और हम अलियासिंग मुद्दों को समझते हैं।

2

static_castबनाम dynamic_castबनाम reinterpret_castआंतरिक एक डाउनकास्ट / upcast पर देखें

इस उत्तर में, मैं इन तीन तंत्रों की तुलना एक ठोस अपकास्ट / डाउनकास्ट उदाहरण पर करता हूं और विश्लेषण करता हूं कि अंतर्निहित बिंदुओं / मेमोरी / असेंबली का क्या होता है, इसकी ठोस समझ देने के लिए कि वे कैसे तुलना करते हैं।

मेरा मानना ​​है कि इससे उन जातियों के अलग होने का एक अच्छा अंतर्ज्ञान होगा:

  • static_cast: एक पता रनटाइम (कम रनटाइम प्रभाव) पर ऑफसेट होता है और कोई सुरक्षा जांच नहीं करता है कि डाउनकास्ट सही है।

  • dyanamic_cast: एक ही पता रनटाइम पर ऑफसेट करता है static_cast, लेकिन यह भी और एक महंगी सुरक्षा जांच है कि एक डाउनकास्ट RTTI का उपयोग करके सही है।

    यह सुरक्षा जांच आपको क्वेरी करने की अनुमति देती है यदि एक बेस क्लास पॉइंटर रनटाइम में दिए गए प्रकार का nullptrहोता है , जिसमें वापसी की जाँच करके एक अमान्य डाउनकास्ट इंगित करता है।

    इसलिए, यदि आपका कोड उसके लिए जाँच करने में सक्षम नहीं है nullptrऔर एक वैध गैर-गर्भपात की कार्रवाई कर रहा है, तो आपको केवल static_castगतिशील कलाकारों के बजाय उपयोग करना चाहिए ।

    यदि एक गर्भपात एकमात्र क्रिया है जो आपका कोड ले सकता है, तो शायद आप केवल dynamic_castडिबग बिल्ड ( -NDEBUG) में सक्षम करना चाहते हैं , और static_castअन्यथा उपयोग करें , जैसे कि यहां किया जाता है , अपने तेज़ रन को धीमा नहीं करने के लिए।

  • reinterpret_cast: रनटाइम पर कुछ भी नहीं करता है, पता भी ऑफसेट नहीं है। सूचक को सही प्रकार की ओर इंगित करना चाहिए, यहां तक ​​कि एक आधार वर्ग भी काम नहीं करता है। आप आम तौर पर यह तब तक नहीं चाहते जब तक कि कच्ची बाइट धाराएँ शामिल न हों।

निम्नलिखित कोड उदाहरण पर विचार करें:

main.cpp

#include <iostream>

struct B1 {
    B1(int int_in_b1) : int_in_b1(int_in_b1) {}
    virtual ~B1() {}
    void f0() {}
    virtual int f1() { return 1; }
    int int_in_b1;
};

struct B2 {
    B2(int int_in_b2) : int_in_b2(int_in_b2) {}
    virtual ~B2() {}
    virtual int f2() { return 2; }
    int int_in_b2;
};

struct D : public B1, public B2 {
    D(int int_in_b1, int int_in_b2, int int_in_d)
        : B1(int_in_b1), B2(int_in_b2), int_in_d(int_in_d) {}
    void d() {}
    int f2() { return 3; }
    int int_in_d;
};

int main() {
    B2 *b2s[2];
    B2 b2{11};
    D *dp;
    D d{1, 2, 3};

    // The memory layout must support the virtual method call use case.
    b2s[0] = &b2;
    // An upcast is an implicit static_cast<>().
    b2s[1] = &d;
    std::cout << "&d           " << &d           << std::endl;
    std::cout << "b2s[0]       " << b2s[0]       << std::endl;
    std::cout << "b2s[1]       " << b2s[1]       << std::endl;
    std::cout << "b2s[0]->f2() " << b2s[0]->f2() << std::endl;
    std::cout << "b2s[1]->f2() " << b2s[1]->f2() << std::endl;

    // Now for some downcasts.

    // Cannot be done implicitly
    // error: invalid conversion from ‘B2*’ to ‘D*’ [-fpermissive]
    // dp = (b2s[0]);

    // Undefined behaviour to an unrelated memory address because this is a B2, not D.
    dp = static_cast<D*>(b2s[0]);
    std::cout << "static_cast<D*>(b2s[0])            " << dp           << std::endl;
    std::cout << "static_cast<D*>(b2s[0])->int_in_d  " << dp->int_in_d << std::endl;

    // OK
    dp = static_cast<D*>(b2s[1]);
    std::cout << "static_cast<D*>(b2s[1])            " << dp           << std::endl;
    std::cout << "static_cast<D*>(b2s[1])->int_in_d  " << dp->int_in_d << std::endl;

    // Segfault because dp is nullptr.
    dp = dynamic_cast<D*>(b2s[0]);
    std::cout << "dynamic_cast<D*>(b2s[0])           " << dp           << std::endl;
    //std::cout << "dynamic_cast<D*>(b2s[0])->int_in_d " << dp->int_in_d << std::endl;

    // OK
    dp = dynamic_cast<D*>(b2s[1]);
    std::cout << "dynamic_cast<D*>(b2s[1])           " << dp           << std::endl;
    std::cout << "dynamic_cast<D*>(b2s[1])->int_in_d " << dp->int_in_d << std::endl;

    // Undefined behaviour to an unrelated memory address because this
    // did not calculate the offset to get from B2* to D*.
    dp = reinterpret_cast<D*>(b2s[1]);
    std::cout << "reinterpret_cast<D*>(b2s[1])           " << dp           << std::endl;
    std::cout << "reinterpret_cast<D*>(b2s[1])->int_in_d " << dp->int_in_d << std::endl;
}

इसके साथ संकलित करें, चलाएं और जुदा करें:

g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp
setarch `uname -m` -R ./main.out
gdb -batch -ex "disassemble/rs main" main.out

जहां setarchहै अक्षम ASLR के लिए इस्तेमाल किया यह आसान रन तुलना करने के लिए बनाने के लिए।

संभव उत्पादन:

&d           0x7fffffffc930
b2s[0]       0x7fffffffc920
b2s[1]       0x7fffffffc940
b2s[0]->f2() 2
b2s[1]->f2() 3
static_cast<D*>(b2s[0])            0x7fffffffc910
static_cast<D*>(b2s[0])->int_in_d  1
static_cast<D*>(b2s[1])            0x7fffffffc930
static_cast<D*>(b2s[1])->int_in_d  3
dynamic_cast<D*>(b2s[0])           0
dynamic_cast<D*>(b2s[1])           0x7fffffffc930
dynamic_cast<D*>(b2s[1])->int_in_d 3
reinterpret_cast<D*>(b2s[1])           0x7fffffffc940
reinterpret_cast<D*>(b2s[1])->int_in_d 32767

अब, जैसा कि उल्लेख किया गया है: https://en.wikipedia.org/wiki/Virtual_method_table वर्चुअल तरीके से कॉल करने के लिए कुशलता से समर्थन करने के लिए, स्मृति डेटा संरचना Dको कुछ इस तरह देखना पड़ता है:

B1:
  +0: pointer to virtual method table of B1
  +4: value of int_in_b1

B2:
  +0: pointer to virtual method table of B2
  +4: value of int_in_b2

D:
  +0: pointer to virtual method table of D (for B1)
  +4: value of int_in_b1
  +8: pointer to virtual method table of D (for B2)
 +12: value of int_in_b2
 +16: value of int_in_d

मुख्य तथ्य यह है कि मेमोरी डेटा संरचना Dमें आंतरिक मेमोरी शामिल होती B1है जो B2आंतरिक रूप से और उस के साथ संगत होती है ।

इसलिए हम महत्वपूर्ण निष्कर्ष पर पहुँचते हैं:

अपस्टॉक या डाउनकास्ट को केवल संकलक समय पर ज्ञात मूल्य द्वारा सूचक मान को स्थानांतरित करना होगा

इस प्रकार, जब Dबेस प्रकार सरणी में पास हो जाता है, तो टाइप कास्ट वास्तव में गणना करता है कि ऑफसेट और कुछ ऐसा दिखता है जो B2स्मृति में बिल्कुल मान्य जैसा दिखता है :

b2s[1] = &d;

सिवाय इसके कि Dइसके बजाय इसके लिए व्यवहार्य है B2, और इसलिए सभी आभासी कॉल पारदर्शी रूप से काम करते हैं।

अब, हम आखिरकार टाइपिंग कास्टिंग और हमारे ठोस उदाहरण के विश्लेषण के लिए वापस आ सकते हैं।

स्टडआउट आउटपुट से हम देखते हैं:

&d           0x7fffffffc930
b2s[1]       0x7fffffffc940

इसलिए, static_castवहां किए गए निहितार्थ ने D0x7fffffc930 पर पूर्ण डेटा संरचना से ऑफसेट की गणना ठीक उसी B2तरह की है जो 0x7fffffc940 पर है। हम यह भी अनुमान लगाते हैं कि 0x7fffffc930 और 0x7fffffffc940 के बीच जो निहित है, वह संभवतः B1डेटा और वाइब्रेट हो सकता है ।

फिर, डाउनकास्ट अनुभागों पर, अब यह समझना आसान है कि अमान्य कैसे विफल होते हैं और क्यों:

  • static_cast<D*>(b2s[0]) 0x7fffffffc910: कंपाइलर केवल 0x10 पर संकलित समय बाइट्स में कोशिश करने और एक B2से युक्त होने के लिए ऊपर चला गयाD

    लेकिन क्योंकि b2s[0]यह नहीं था D, यह अब एक अपरिभाषित स्मृति क्षेत्र की ओर इशारा करता है।

    Disassembly है:

    49          dp = static_cast<D*>(b2s[0]);
       0x0000000000000fc8 <+414>:   48 8b 45 d0     mov    -0x30(%rbp),%rax
       0x0000000000000fcc <+418>:   48 85 c0        test   %rax,%rax
       0x0000000000000fcf <+421>:   74 0a   je     0xfdb <main()+433>
       0x0000000000000fd1 <+423>:   48 8b 45 d0     mov    -0x30(%rbp),%rax
       0x0000000000000fd5 <+427>:   48 83 e8 10     sub    $0x10,%rax
       0x0000000000000fd9 <+431>:   eb 05   jmp    0xfe0 <main()+438>
       0x0000000000000fdb <+433>:   b8 00 00 00 00  mov    $0x0,%eax
       0x0000000000000fe0 <+438>:   48 89 45 98     mov    %rax,-0x68(%rbp)

    इसलिए हम देखते हैं कि जीसीसी करता है:

    • जांचें कि क्या सूचक NULL है, और यदि हां NULL वापस
    • अन्यथा, इसे 0x10 से घटाकर उस तक पहुँचने के लिए Dजो मौजूद नहीं है
  • dynamic_cast<D*>(b2s[0]) 0: C ++ ने वास्तव में पाया कि कास्ट अमान्य था और वापस आ गया nullptr!

    ऐसा कोई तरीका नहीं है जिससे संकलन समय पर किया जा सके, और हम इस बात की पुष्टि करेंगे:

    59          dp = dynamic_cast<D*>(b2s[0]);
       0x00000000000010ec <+706>:   48 8b 45 d0     mov    -0x30(%rbp),%rax
       0x00000000000010f0 <+710>:   48 85 c0        test   %rax,%rax
       0x00000000000010f3 <+713>:   74 1d   je     0x1112 <main()+744>
       0x00000000000010f5 <+715>:   b9 10 00 00 00  mov    $0x10,%ecx
       0x00000000000010fa <+720>:   48 8d 15 f7 0b 20 00    lea    0x200bf7(%rip),%rdx        # 0x201cf8 <_ZTI1D>
       0x0000000000001101 <+727>:   48 8d 35 28 0c 20 00    lea    0x200c28(%rip),%rsi        # 0x201d30 <_ZTI2B2>
       0x0000000000001108 <+734>:   48 89 c7        mov    %rax,%rdi
       0x000000000000110b <+737>:   e8 c0 fb ff ff  callq  0xcd0 <__dynamic_cast@plt>
       0x0000000000001110 <+742>:   eb 05   jmp    0x1117 <main()+749>
       0x0000000000001112 <+744>:   b8 00 00 00 00  mov    $0x0,%eax
       0x0000000000001117 <+749>:   48 89 45 98     mov    %rax,-0x68(%rbp)

    पहले एक NULL चैक होता है, और यह NULL लौटता है यदि वें einput NULL है।

    अन्यथा, यह आरडीएक्स, आरएसआई और आरडीआई और कॉल में कुछ तर्क देता है __dynamic_cast

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

    इसलिए इसे B2उस तालिका के लिए प्रविष्टि से शुरू करना चाहिए , फिर इस वर्ग पदानुक्रम तक चलना चाहिए जब तक कि यह पता नहीं चलता कि Dटाइप टाइपकास्ट के लिए व्यवहार्य है b2s[0]

    यही कारण है कि पुनर्व्याख्या कास्ट संभावित रूप से महंगा है! यहाँ एक उदाहरण है जहाँ एक जटिल प्रक्रिया में एक में एक लाइनर पैच को परिवर्तित dynamic_castकरने से static_castरनटाइम में 33% की कमी आई है!

  • reinterpret_cast<D*>(b2s[1]) 0x7fffffffc940यह सिर्फ हमें आँख बंद करके विश्वास करता है: हमने कहा कि एक Dपते पर है b2s[1], और संकलक कोई ऑफसेट गणना नहीं करता है।

    लेकिन यह गलत है, क्योंकि डी वास्तव में 0x7fffffc930 पर है, जो 0x7fffffffc940 पर है वह डी के अंदर बी 2 जैसी संरचना है! तो कचरा पहुंच जाता है।

    हम इस भयावह -O0सभा से इसकी पुष्टि कर सकते हैं जो मूल्य को चारों ओर ले जाती है:

    70          dp = reinterpret_cast<D*>(b2s[1]);
       0x00000000000011fa <+976>:   48 8b 45 d8     mov    -0x28(%rbp),%rax
       0x00000000000011fe <+980>:   48 89 45 98     mov    %rax,-0x68(%rbp)

संबंधित सवाल:

Ubuntu 18.04 am6464, GCC 7.4.0 पर परीक्षण किया गया।

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