c++ - Static_cast, dynamic_cast, const_cast और reinterpret_cast का उपयोग कब किया जाना चाहिए?
pointers casting (5)
अब तक के अन्य उत्तरों के अलावा, यहां अनोखा उदाहरण है जहां static_cast
पर्याप्त नहीं है ताकि reinterpret_cast
की आवश्यकता हो। मान लीजिए कि एक ऐसा फ़ंक्शन है जो आउटपुट पैरामीटर में पॉइंटर्स को विभिन्न वर्गों की ऑब्जेक्ट देता है (जो एक सामान्य बेस क्लास साझा नहीं करते हैं)। इस तरह के फ़ंक्शन का वास्तविक उदाहरण CoCreateInstance()
(अंतिम पैरामीटर देखें, जो वास्तव में void**
)। मान लीजिए कि आप इस फ़ंक्शन से ऑब्जेक्ट की विशेष श्रेणी का अनुरोध करते हैं, इसलिए आप पॉइंटर के लिए अग्रिम रूप से जानते हैं (जिसे आप अक्सर COM ऑब्जेक्ट्स के लिए करते हैं)। इस मामले में आप static_cast
साथ पॉइंटर को अपने पॉइंटर में void**
नहीं डाल सकते हैं void**
आपको 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
-
dynamic_cast
-
const_cast
-
reinterpret_cast
- सी-शैली कास्ट
(type)value
- फंक्शन-शैली कास्ट
type(value)
एक निर्णय कैसे तय करता है कि किस विशिष्ट मामलों में उपयोग करना है?
क्या this आपके प्रश्न का उत्तर देता है?
मैंने कभी भी reinterpret_cast
उपयोग नहीं किया है, और आश्चर्य है कि किसी ऐसे मामले में चल रहा है जिसकी आवश्यकता है, खराब डिजाइन की गंध नहीं है। कोड बेस में मैं dynamic_cast
पर काम करता हूं। static_cast
साथ अंतर यह है कि एक dynamic_cast
रनटाइम जांच करता है जो (सुरक्षित) हो सकता है या नहीं (अधिक ओवरहेड) जो आप चाहते हैं ( msdn देखें)।
यदि आप आंतरिक रूप से कुछ जानते हैं तो यह मदद कर सकता है ...
static_cast
- सी ++ कंपाइलर पहले से ही जानता है कि कैसे स्केलर प्रकारों के बीच कनवर्ट करना है जैसे फ्लोट टू इंट। उनके लिए
static_cast
प्रयोग करें। - जब आप टाइप
A
सेB
कनवर्ट करने के लिए कंपाइलर से पूछते हैं, तोstatic_cast
कॉलB
के कन्स्ट्रक्टर कोA
को पैरा के रूप में पास करता है। वैकल्पिक रूप से,A
में एक रूपांतरण ऑपरेटर हो सकता है (यानीA::operator B()
)। यदिB
में ऐसे कंस्ट्रक्टर नहीं हैं, याA
में एक रूपांतरण ऑपरेटर नहीं है, तो आपको संकलन समय त्रुटि मिलती है। -
A*
सेB*
तक कास्ट हमेशा सफल होता है यदि ए और बी विरासत पदानुक्रम (या शून्य) में हैं अन्यथा आपको संकलन त्रुटि मिलती है। - गोटो : यदि आपने मूल पॉइंटर को व्युत्पन्न सूचक पर डाला है, लेकिन यदि वास्तविक वस्तु वास्तव में व्युत्पन्न प्रकार नहीं है तो आपको त्रुटि नहीं मिलती है। आपको खराब पॉइंटर मिलता है और रनटाइम पर एक सेगफॉल्ट की संभावना होती है।
A&
B&
लिए भी जाता है। - गोटो : व्युत्पन्न से बेस या उपरोक्त से कास्ट नई प्रतिलिपि बनाता है! सी # / जावा से आने वाले लोगों के लिए, यह एक बड़ा आश्चर्य हो सकता है क्योंकि परिणाम मूल रूप से व्युत्पन्न से बनाई गई एक कटा हुआ वस्तु है।
dynamic_cast
- डायनामिक_कास्ट रनटाइम प्रकार की जानकारी का उपयोग यह पता लगाने के लिए करता है कि क्या कास्ट वैध है या नहीं। उदाहरण के लिए,
(Base*)
से(Derived*)
विफल हो सकता है यदि सूचक वास्तव में व्युत्पन्न प्रकार का नहीं है। - इसका मतलब है, static_cast की तुलना में गतिशील_कास्ट बहुत महंगा है!
-
A*
सेB*
, यदि कास्ट अमान्य है तो dynamic_cast nullptr लौटाएगा। -
A&
B&
यदि कास्ट अमान्य है तो dynamic_cast bad_cast अपवाद फेंक देगा। - अन्य जानवरों के विपरीत, रनटाइम ओवरहेड है।
const_cast
- जबकि static_cast गैर-आधार को कॉन्स्ट करने के लिए कर सकता है, यह अन्य तरीकों से नहीं जा सकता है। Const_cast दोनों तरीकों से कर सकता है।
- एक उदाहरण जहां यह आसान आता है, कुछ कंटेनर जैसे
set<T>
माध्यम से पुनरावृत्ति कर रहा है जो केवल यह सुनिश्चित करने के लिए अपने तत्वों को स्थिर करता है कि आप इसकी कुंजी नहीं बदलते हैं। हालांकि यदि आपका इरादा ऑब्जेक्ट के गैर-कुंजी सदस्यों को संशोधित करना है तो यह ठीक होना चाहिए। आप स्थिरता को हटाने के लिए const_cast का उपयोग कर सकते हैं। - एक और उदाहरण यह है कि जब आप
T& foo()
साथ-साथconst T& foo()
को कार्यान्वित करना चाहते हैं। कोड डुप्लिकेशन से बचने के लिए, आप एक समारोह से दूसरे फ़ंक्शन के मूल्य को वापस करने के लिए const_cast लागू कर सकते हैं।
reinterpret_cast
- यह मूल रूप से कहता है कि इस स्मृति स्थान पर इन बाइट्स को लें और इसे दिए गए ऑब्जेक्ट के रूप में सोचें।
- उदाहरण के लिए, आप फ्लोट में बिट्स की तरह दिखने के लिए 4 बाइट्स फ्लोट को 4 बाइट्स तक लोड कर सकते हैं।
- जाहिर है, यदि डेटा इस प्रकार के लिए सही नहीं है, तो आप segfault प्राप्त कर सकते हैं।
- इस कलाकार के लिए कोई रनटाइम ओवरहेड नहीं है।
विरासत पदानुक्रम के भीतर पॉइंटर्स / संदर्भों को परिवर्तित करने के लिए dynamic_cast
उपयोग करें।
सामान्य प्रकार के रूपांतरणों के लिए static_cast
प्रयोग करें।
बिट पैटर्न के निम्न-स्तर पुनर्वितरण के लिए reinterpret_cast
उपयोग करें। अत्यधिक सावधानी के साथ प्रयोग करें।
दूर const_cast
const/volatile
कास्टिंग के लिए const_cast
प्रयोग करें। तब तक इससे बचें जब तक कि आप एक कॉन्स्ट-गलत API का उपयोग करके फंस गए हों।
static_cast
वह पहला कलाकार है जिसका उपयोग करने का प्रयास करना चाहिए। यह चीजों के बीच अंतर्निहित रूपांतरण की तरह चीजें करता है (जैसे कि int
float
, या void*
पॉइंटर void*
), और यह स्पष्ट रूपांतरण फ़ंक्शंस (या निहित) को भी कॉल कर सकता है। कई मामलों में, स्पष्ट रूप से static_cast
बताते हुए आवश्यक नहीं है, लेकिन यह ध्यान रखना महत्वपूर्ण है कि T(something)
वाक्यविन्यास T(something)
(T)something
बराबर (T)something
और इससे बचा जाना चाहिए (उस पर और अधिक)। एक T(something, something_else)
सुरक्षित है, हालांकि, और कन्स्ट्रक्टर को कॉल करने की गारंटी है।
static_cast
विरासत पदानुक्रमों के माध्यम से भी जा सकता है। जब ऊपर कास्टिंग (बेस क्लास की ओर) कास्टिंग करते समय अनावश्यक होता है, लेकिन जब नीचे कास्टिंग किया जाता है तब तक इसका उपयोग तब तक किया जा सकता है जब तक यह virtual
विरासत के माध्यम से नहीं डाला जाता है। यह जांच नहीं करता है, हालांकि, और यह एक प्रकार के पदानुक्रम के नीचे static_cast
को अपरिभाषित व्यवहार है जो वास्तव में वस्तु का प्रकार नहीं है।
const_cast
का उपयोग किसी चर को हटाने या जोड़ने के लिए किया जा सकता है; कोई अन्य सी ++ कास्ट इसे हटाने में सक्षम नहीं है (यहां तक कि reinterpret_cast
भी नहीं)। यह ध्यान रखना महत्वपूर्ण है कि पूर्व में const
को संशोधित करना केवल मूल चर है यदि मूल चर है; यदि आप इसे किसी ऐसे संदर्भ के संदर्भ में बंद करने के लिए उपयोग करते हैं जिसे const
साथ घोषित नहीं किया गया था, तो यह सुरक्षित है। उदाहरण के लिए, const
पर आधारित सदस्य फ़ंक्शंस को अधिभारित करते समय यह उपयोगी हो सकता है। इसका उपयोग ऑब्जेक्ट में const
जोड़ने के लिए भी किया जा सकता है, जैसे कि सदस्य फ़ंक्शन ओवरलोड को कॉल करना।
const_cast
भी volatile
पर समान रूप से काम करता है, हालांकि यह कम आम है।
dynamic_cast
लगभग पूरी तरह से बहुरूपता को संभालने के लिए उपयोग किया जाता है। आप किसी भी अन्य प्रकार के किसी भी प्रकार के पॉलीमोर्फिक प्रकार के पॉइंटर या संदर्भ को कास्ट कर सकते हैं (एक पॉलिमॉर्फिक प्रकार में कम से कम एक वर्चुअल फ़ंक्शन, घोषित या विरासत में मिला है)। आप इसे नीचे कास्टिंग करने से अधिक के लिए उपयोग कर सकते हैं - आप किनारे या यहां तक कि एक और श्रृंखला भी डाल सकते हैं। dynamic_cast
वांछित वस्तु की तलाश करेगा और यदि संभव हो तो इसे वापस कर देगा। यदि यह नहीं हो सकता है, तो यह एक सूचक के मामले में nullptr
वापस करेगा, या एक संदर्भ के मामले में std::bad_cast
फेंक std::bad_cast
।
हालांकि, dynamic_cast
में कुछ सीमाएं हैं। यह काम नहीं करता है अगर विरासत पदानुक्रम (तथाकथित 'डरावना हीरा') में एक ही प्रकार की कई वस्तुएं हैं और आप virtual
विरासत का उपयोग नहीं कर रहे हैं। यह केवल सार्वजनिक विरासत के माध्यम से जा सकता है - यह हमेशा protected
या private
विरासत के माध्यम से यात्रा करने में असफल रहेगा। यह शायद ही कभी एक मुद्दा है, हालांकि, विरासत के इस तरह के रूप दुर्लभ हैं।
reinterpret_cast
सबसे खतरनाक कलाकार है, और बहुत कम इस्तेमाल किया जाना चाहिए। यह एक प्रकार को सीधे दूसरे में बदल देता है - जैसे कि एक पॉइंटर से दूसरे में मूल्य कास्टिंग, या एक इंटरेक्टर में एक पॉइंटर संग्रहीत करना, या अन्य गंदे चीजों के सभी प्रकार। बड़े पैमाने पर, 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
प्रदर्शन करते समय अभिगम नियंत्रण को अनदेखा करता है, जिसका अर्थ है कि उनके पास एक ऐसा ऑपरेशन करने की क्षमता है जो कोई अन्य कलाकार नहीं कर सकता है। यह ज्यादातर क्लेज है, हालांकि, और मेरे दिमाग में सी-स्टाइल कास्ट से बचने का एक और कारण है।