c++ - नियमित कलाकार बनाम static_cast बनाम गतिशील_कास्ट




pointers casting (6)

इस प्रश्न का उत्तर यहां दिया गया है:

मैं लगभग बीस वर्षों के लिए सी और सी ++ कोड लिख रहा हूं, लेकिन इन भाषाओं का एक पहलू है जिसे मैंने कभी नहीं समझा है। मैंने स्पष्ट रूप से नियमित रूप से इस्तेमाल किया है यानी

MyClass *m = (MyClass *)ptr;

पूरे स्थान पर, लेकिन दो अन्य प्रकार के कास्ट प्रतीत होते हैं, और मुझे अंतर नहीं पता है। कोड की निम्नलिखित पंक्तियों के बीच क्या अंतर है?

MyClass *m = (MyClass *)ptr;
MyClass *m = static_cast<MyClass *>(ptr);
MyClass *m = dynamic_cast<MyClass *>(ptr);

static_cast

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

void func(void *data) {
  // Conversion from MyClass* -> void* is implicit
  MyClass *c = static_cast<MyClass*>(data);
  ...
}

int main() {
  MyClass c;
  start_thread(&func, &c)  // func(&c) will be called
      .join();
}

इस उदाहरण में, आप जानते हैं कि आपने एक MyClass ऑब्जेक्ट पारित किया है, और इस प्रकार यह सुनिश्चित करने के लिए रनटाइम चेक की आवश्यकता नहीं है।

dynamic_cast

dynamic_cast उपयोगी होता है जब आप नहीं जानते कि ऑब्जेक्ट का गतिशील प्रकार क्या है। यह एक शून्य सूचक देता है यदि संदर्भित ऑब्जेक्ट में बेस क्लास के रूप में डाला गया प्रकार नहीं है (जब आप किसी संदर्भ में bad_cast जाते हैं, तो उस मामले में bad_cast अपवाद फेंक दिया जाता है)।

if (JumpStm *j = dynamic_cast<JumpStm*>(&stm)) {
  ...
} else if (ExprStm *e = dynamic_cast<ExprStm*>(&stm)) {
  ...
}

यदि आप डाउनकास्ट (व्युत्पन्न कक्षा में डाले गए हैं) और तर्क प्रकार polymorphic नहीं है, तो आप dynamic_cast उपयोग नहीं कर सकते हैं। उदाहरण के लिए, निम्न कोड मान्य नहीं है, क्योंकि Base में कोई वर्चुअल फ़ंक्शन नहीं है:

struct Base { };
struct Derived : Base { };
int main() {
  Derived d; Base *b = &d;
  dynamic_cast<Derived*>(b); // Invalid
}

एक "अप-कास्ट" (बेस क्लास में डाला गया) हमेशा static_cast और dynamic_cast दोनों के साथ मान्य है, और बिना किसी कलाकार के, "अप-कास्ट" एक अंतर्निहित रूपांतरण है।

नियमित कास्ट

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

इसके अलावा, सी-स्टाइल न केवल आपको ऐसा करने की इजाजत देता है, बल्कि वे आपको एक निजी बेस-क्लास में सुरक्षित रूप से डालने की अनुमति भी देते हैं, जबकि "समकक्ष" static_cast अनुक्रम आपको इसके लिए संकलन-समय त्रुटि देगा।

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


स्टेटिक कास्ट

स्थैतिक कलाकार संगत प्रकारों के बीच रूपांतरण करता है। यह सी-स्टाइल कास्ट जैसा ही है, लेकिन यह अधिक प्रतिबंधित है। उदाहरण के लिए, सी-स्टाइल कास्ट एक पूर्णांक सूचक को चार को इंगित करने की अनुमति देगा।

char c = 10;       // 1 byte
int *p = (int*)&c; // 4 bytes

चूंकि यह 4-बाइट पॉइंटर आवंटित स्मृति के 1 बाइट को इंगित करता है, इस पॉइंटर को लिखने से या तो रन-टाइम त्रुटि हो जाएगी या कुछ आसन्न स्मृति को ओवरराइट कर दिया जाएगा।

*p = 5; // run-time error: stack corruption

सी-स्टाइल कास्ट के विपरीत, स्थैतिक कलाकार संकलक को यह जांचने की अनुमति देगा कि पॉइंटर और पॉइंट डेटा प्रकार संगत हैं, जो प्रोग्रामर को संकलन के दौरान इस गलत सूचक असाइनमेंट को पकड़ने की अनुमति देता है।

int *q = static_cast<int*>(&c); // compile-time error

कास्ट दोहराएं

पॉइंटर रूपांतरण को मजबूर करने के लिए, उसी तरह सी-स्टाइल कास्ट पृष्ठभूमि में करता है, इसके बजाय कास्ट फिर से परिभाषित किया जाएगा।

int *r = reinterpret_cast<int*>(&c); // forced conversion

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

गतिशील कास्ट

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

गतिशील कास्ट उदाहरण

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

class MyBase 
{ 
  public:
  virtual void test() {}
};
class MyChild : public MyBase {};



int main()
{
  MyChild *child = new MyChild();
  MyBase  *base = dynamic_cast<MyBase*>(child); // ok
}

अगला उदाहरण माईबेस पॉइंटर को माई चाइल्ड पॉइंटर में बदलने का प्रयास करता है। चूंकि बेस ऑब्जेक्ट में पूर्ण बाल ऑब्जेक्ट नहीं है, इसलिए यह सूचक रूपांतरण विफल हो जाएगा। इसे इंगित करने के लिए, गतिशील कास्ट एक शून्य सूचक देता है। यह जांचने का एक सुविधाजनक तरीका देता है कि रन-टाइम के दौरान रूपांतरण सफल हुआ है या नहीं।

MyBase  *base = new MyBase();
MyChild *child = dynamic_cast<MyChild*>(base);


if (child == 0) 
std::cout << "Null pointer returned";

यदि एक सूचक के बजाय संदर्भ परिवर्तित किया जाता है, तो गतिशील कास्ट फिर खराब_कास्ट अपवाद फेंकने में विफल हो जाएगा। इसे एक कोशिश-पकड़ विवरण का उपयोग करके संभाला जाना चाहिए।

#include <exception>
// …  
try
{ 
  MyChild &child = dynamic_cast<MyChild&>(*base);
}
catch(std::bad_cast &e) 
{ 
  std::cout << e.what(); // bad dynamic_cast
}

गतिशील या स्थिर कास्ट

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

MyBase *base = static_cast<MyBase*>(child); // ok

हालांकि, दूसरे उदाहरण में रूपांतरण या तो सफल हो सकता है या विफल हो सकता है। यह विफल हो जाएगा यदि MyBase ऑब्जेक्ट में MyBase इंस्टेंस है और यदि यह MyChild इंस्टेंस है तो यह सफल होगा। कुछ स्थितियों में यह रन-टाइम तक ज्ञात नहीं हो सकता है। जब यह मामला गतिशील कास्ट स्थिर कास्ट की तुलना में बेहतर विकल्प है।

// Succeeds for a MyChild object
MyChild *child = dynamic_cast<MyChild*>(base);

यदि आधार-से-व्युत्पन्न रूपांतरण गतिशील कलाकार के बजाय एक स्थिर कलाकार का उपयोग करके किया गया था तो रूपांतरण विफल नहीं होता। यह एक सूचक वापस लौटा होगा जो एक अपूर्ण वस्तु को संदर्भित करता है। ऐसे पॉइंटर को डिफ्रेंस करने से रन-टाइम त्रुटियां हो सकती हैं।

// Allowed, but invalid
MyChild *child = static_cast<MyChild*>(base);

// Incomplete MyChild object dereferenced
(*child);

कास्ट कास्ट

यह मुख्य रूप से एक चर के कॉन्स्ट संशोधक को जोड़ने या हटाने के लिए प्रयोग किया जाता है।

const int myConst = 5;
int *nonConst = const_cast<int*>(&myConst); // removes const

यद्यपि कॉन्स कास्ट स्थिर होने के मूल्य को बदलने की इजाजत देता है, ऐसा करने से अभी भी अमान्य कोड है जो रन-टाइम त्रुटि का कारण बन सकता है। यह उदाहरण के लिए हो सकता है अगर निरंतर पढ़ने-योग्य स्मृति के एक खंड में स्थित था।

*nonConst = 10; // potential run-time error

कॉन्स कास्ट का उपयोग मुख्य रूप से तब किया जाता है जब एक ऐसा फ़ंक्शन होता है जो गैर-स्थिर पॉइंटर तर्क लेता है, भले ही यह पॉइंट को संशोधित नहीं करता है।

void print(int *p) 
{
   std::cout << *p;
}

फ़ंक्शन को कॉन्स कास्ट का उपयोग करके स्थिर चर पारित किया जा सकता है।

print(&myConst); // error: cannot convert 
                 // const int* to int*

print(nonConst); // allowed

स्रोत और अधिक स्पष्टीकरण


एफवाईआई, मेरा मानना ​​है कि बजेर्न स्ट्राउस्ट्रप को यह कहते हुए उद्धृत किया गया है कि सी-स्टाइल कास्ट टाला जाना चाहिए और यदि संभव हो तो आपको static_cast या dynamic_cast का उपयोग करना चाहिए।

बार्ने स्ट्राउस्ट्रप की सी ++ स्टाइल एफएक्यू

आप जो चाहते हैं उसके लिए वह सलाह लें। मैं एक सी ++ गुरु होने से बहुत दूर हूं।


सी-शैली conflate const_cast, static_cast, और reinterpret_cast है।

मेरी इच्छा है कि सी ++ में सी-स्टाइल कास्ट न हो। सी ++ कास्ट ठीक से खड़ा होता है (जैसा कि उन्हें करना चाहिए; आम तौर पर कुछ बुरा करने का संकेत होता है) और प्रदर्शन के विभिन्न प्रकार के रूपांतरणों के बीच सही ढंग से अंतर करते हैं। वे समान दिखने वाले कार्यों को भी लिखने की अनुमति देते हैं, उदाहरण के लिए boost :: lexical_cast, जो एक स्थिरता परिप्रेक्ष्य से काफी अच्छा है।


dynamic_cast केवल सूचक और संदर्भ प्रकार का समर्थन करता है। यदि कच्चे असंभव है तो यदि कच्चे असंभव है तो टाइप असंभव है या यदि कोई संदर्भ प्रकार है तो अपवाद फेंकता है। इसलिए, dynamic_cast का उपयोग यह जांचने के लिए किया जा सकता है कि कोई वस्तु किसी दिए गए प्रकार का है या नहीं, static_cast नहीं कर सकता (आप केवल एक अवैध मान के साथ समाप्त हो जाएंगे)।

सी-शैली (और अन्य) कास्ट अन्य उत्तरों में शामिल किया गया है।


dynamic_cast में रनटाइम प्रकार की जांच होती है और केवल संदर्भ और पॉइंटर्स के साथ काम करती है, जबकि static_cast रनटाइम प्रकार की जांच की पेशकश नहीं करता है। पूरी जानकारी के लिए, एमएसडीएन आलेख static_cast ऑपरेटर देखें





casting