C ++ में डायनेमिक_कास्ट और स्टेटिक_कास्ट


155

मैं dynamic_castC ++ में कीवर्ड से काफी भ्रमित हूं ।

struct A {
    virtual void f() { }
};
struct B : public A { };
struct C { };

void f () {
    A a;
    B b;

    A* ap = &b;
    B* b1 = dynamic_cast<B*> (&a);  // NULL, because 'a' is not a 'B'
    B* b2 = dynamic_cast<B*> (ap);  // 'b'
    C* c = dynamic_cast<C*> (ap);   // NULL.

    A& ar = dynamic_cast<A&> (*ap); // Ok.
    B& br = dynamic_cast<B&> (*ap); // Ok.
    C& cr = dynamic_cast<C&> (*ap); // std::bad_cast
}

परिभाषा कहती है:

dynamic_castकीवर्ड एक से दूसरे सूचक या संदर्भ प्रकार से एक गृहीत डाले, कलाकारों की वैधता सुनिश्चित करने के लिए एक क्रम की जांच प्रदर्शन

क्या हम dynamic_castC ++ में C ++ के बराबर लिख सकते हैं ताकि मैं चीजों को बेहतर ढंग से समझ सकूं?


1
यदि आप एक अच्छा विचार प्राप्त करना चाहते हैं dynamic_cast<>कि पर्दे के पीछे कैसे काम करता है (या C ++ कितना काम करता है), एक अच्छी किताब (जो कि कुछ तकनीकी के लिए पढ़ना बहुत आसान है) है Lippman का "इनसाइड द C ++ ऑब्जेक्ट मॉडल"। इसके अलावा स्ट्रॉस्ट्रुप की "डिजाइनिंग एंड एवोल्यूशन ऑफ सी ++" और "द सी ++ प्रोग्रामिंग लैंग्वेज" किताबें अच्छे संसाधन हैं, लेकिन लिपमैन की किताब 'सी + +' पर्दे के पीछे कैसे काम करती है, इसके लिए समर्पित है।
माइकल बूर

लाइन में टिप्पणी का क्या B* b2 = dynamic_cast<B*> (ap) // 'b'मतलब है? b2 is pointer to bऔर क्या?
LRDPRDX

@BogdanSikach क्या सवाल है? इसका

जवाबों:


282

यहाँ पर एक प्रकार का जहाज़ है static_cast<>और dynamic_cast<>विशेष रूप से वे संकेत से संबंधित हैं। यह सिर्फ 101-स्तरीय रंडाउन है, यह सभी पेचीदगियों को कवर नहीं करता है।

static_cast <टाइप *> (ptr)

यह पॉइंटर को अंदर ले जाता है ptrऔर सुरक्षित रूप से टाइप के पॉइंटर में डालने की कोशिश करता है Type*। यह कलाकार संकलन के समय किया जाता है। यह केवल कलाकारों का प्रदर्शन करेगा यदि प्रकार प्रकार संबंधित हैं। यदि प्रकार संबंधित नहीं हैं, तो आपको संकलक त्रुटि मिलेगी। उदाहरण के लिए:

class B {};
class D : public B {};
class X {};

int main()
{
  D* d = new D;
  B* b = static_cast<B*>(d); // this works
  X* x = static_cast<X*>(d); // ERROR - Won't compile
  return 0;
}

डायनामिक_कास्ट <टाइप *> (पीटीआर)

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

जातियां दो दिशाओं में से एक में जा सकती हैं: आधार से व्युत्पन्न (बी 2 डी) या व्युत्पन्न से आधार (डी 2 बी) तक। यह देखना काफी आसान है कि D2B कास्ट रनटाइम पर कैसे काम करेगा। या तो ptrसे लिया गया था Typeया यह नहीं था। D2B डायनेमिक_कास्ट <> s के मामले में, नियम सरल हैं। आप किसी भी चीज़ के लिए कुछ भी करने की कोशिश कर सकते हैं, और अगर ptrवास्तव में इससे प्राप्त हुआ था Type, तो आपको एक Type*पॉइंटर वापस मिलेगा dynamic_cast। अन्यथा, आपको एक NULL पॉइंटर मिलेगा।

लेकिन बी 2 डी कास्ट थोड़ा अधिक जटिल है। निम्नलिखित कोड पर विचार करें:

#include <iostream>
using namespace std;

class Base
{
public:
    virtual void DoIt() = 0;    // pure virtual
    virtual ~Base() {};
};

class Foo : public Base
{
public:
    virtual void DoIt() { cout << "Foo"; }; 
    void FooIt() { cout << "Fooing It..."; }
};

class Bar : public Base
{
public :
    virtual void DoIt() { cout << "Bar"; }
    void BarIt() { cout << "baring It..."; }
};

Base* CreateRandom()
{
    if( (rand()%2) == 0 )
        return new Foo;
    else
        return new Bar;
}


int main()
{
    for( int n = 0; n < 10; ++n )
    {
        Base* base = CreateRandom();

            base->DoIt();

        Bar* bar = (Bar*)base;
        bar->BarIt();
    }
  return 0;
}

main()यह नहीं बता सकता है कि किस प्रकार की वस्तु CreateRandom()वापस आएगी, इसलिए सी-स्टाइल कास्ट Bar* bar = (Bar*)base;निश्चित रूप से टाइप-सुरक्षित नहीं है। आप इसे कैसे ठीक कर सकते हैं? एक तरीका यह bool की तरह एक समारोह को जोड़ने के लिए किया जाएगा AreYouABar() const = 0;आधार वर्ग के लिए और वापसी trueसे Barऔर falseसे Foo। लेकिन एक और तरीका है: उपयोग dynamic_cast<>:

int main()
{
    for( int n = 0; n < 10; ++n )
    {
        Base* base = CreateRandom();

        base->DoIt();

        Bar* bar = dynamic_cast<Bar*>(base);
        Foo* foo = dynamic_cast<Foo*>(base);
        if( bar )
            bar->BarIt();
        if( foo )
            foo->FooIt();
    }
  return 0;

}

कलाकार रनटाइम पर निष्पादित करते हैं, और ऑब्जेक्ट को क्वेरी करके काम करते हैं (अब के लिए चिंता करने की कोई ज़रूरत नहीं है), यह पूछते हुए कि क्या यह वह प्रकार है जिसकी हम तलाश कर रहे हैं। यदि यह है, dynamic_cast<Type*>एक सूचक लौटाता है; अन्यथा यह NULL को लौटाता है।

बेस-टू-व्युत्पन्न कास्टिंग का उपयोग करने के लिए काम करने के लिए dynamic_cast<>, बेस, फू और बार होना चाहिए जो मानक कॉल पॉलीमॉर्फिक प्रकार कहते हैं । बहुरूपी प्रकार होने के लिए, आपकी कक्षा में कम से कम एक virtualकार्य होना चाहिए । यदि आपकी कक्षाएं बहुरूपी प्रकार नहीं हैं, तो dynamic_castवसीयत का आधार-से-व्युत्पन्न उपयोग संकलित नहीं होगा। उदाहरण:

class Base {};
class Der : public Base {};


int main()
{
    Base* base = new Der;
    Der* der = dynamic_cast<Der*>(base); // ERROR - Won't compile

    return 0;
}

आधार के लिए एक आभासी फ़ंक्शन जोड़ना, जैसे कि वर्चुअल डोर, बेस और डेर पॉलीमॉर्फिक दोनों प्रकार बनाएगा:

class Base 
{
public:
    virtual ~Base(){};
};
class Der : public Base {};


int main()
{
    Base* base = new Der;
    Der* der = dynamic_cast<Der*>(base); // OK

    return 0;
}

9
संकलक इसके बारे में पहले से शिकायत क्यों करता है? और जब हम केवल आधार के लिए केवल एक आभासी dctor प्रदान करते हैं?
रीका

5
यह ध्यान दिया जाना चाहिए कि यदि आप करते हैं Base* base = new Base;, तो dynamic_cast<Foo*>(base)होगा NULL
Y29295

2
@ कोडरक्स 7 डायनेमिक_कास्ट को रन-टाइम टाइप इंफॉर्मेशन (आरटीटीआई) की जरूरत है जो केवल उन वर्गों के लिए उपलब्ध है जो पॉलीमॉर्फिक हैं, यानी कम से कम एक वर्चुअल पद्धति वाली कक्षाएं।
एल्वॉर्फिरिल्मथ्रेडिया 21

@ Yay295 के dynamic_cast<Foo*>(base)मामले में अशक्त क्यों है Base* base = new Base;?
मुनेशसिंह

3
@ मुनेश क्योंकि baseनहीं है Foo। एक Baseसूचक इंगित कर सकता है Foo, लेकिन यह अभी भी एक है Foo, इसलिए एक गतिशील कलाकार काम करेगा। यदि आप करते हैं Base* base = new Base, baseएक है Base, एक नहीं है Foo, तो आप गतिशील रूप से इसे नहीं डाल सकते हैं Foo
Yay295

20

जब तक आप अपने स्वयं के हाथ से लुढ़का आरटीटीआई (और सिस्टम एक को दरकिनार) लागू नहीं कर रहे हैं, तब तक dynamic_castसीधे सी ++ उपयोगकर्ता-स्तरीय कोड को लागू करना संभव नहीं है । dynamic_castबहुत सी + + कार्यान्वयन के RTTI प्रणाली में बंधा हुआ है।

लेकिन, आपको आरटीटीआई (और इस प्रकार dynamic_cast) को और अधिक समझने में मदद करने के लिए , आपको <typeinfo>हेडर, और typeidऑपरेटर पर पढ़ना चाहिए । यह आपके द्वारा हाथ में लिए गए ऑब्जेक्ट के अनुरूप प्रकार की जानकारी देता है, और आप इन प्रकार की जानकारी ऑब्जेक्ट से विभिन्न (सीमित) चीजों की पूछताछ कर सकते हैं।


मैं आपको विकिपीडिया की ओर संकेत करूंगा, लेकिन आरटीटीआई पर इसके लेख और dynamic_castबहुत कंजूसी के हैं। :-P बस अपने आप से खेलते हैं जब तक आप इसे लटका नहीं लेते। :-)
क्रिस जस्टर-यंग

10

सी में कोड से अधिक, मुझे लगता है कि एक अंग्रेजी परिभाषा पर्याप्त हो सकती है:

एक वर्ग के आधार को देखते हुए एक व्युत्पन्न वर्ग व्युत्पन्न है, dynamic_castएक बेस पॉइंटर को एक व्युत्पन्न पॉइंटर में बदल देगा यदि और केवल अगर वास्तविक वस्तु को इंगित किया गया है तो वास्तव में एक व्युत्पन्न वस्तु है।

class Base { virtual ~Base() {} };
class Derived : public Base {};
class Derived2 : public Base {};
class ReDerived : public Derived {};

void test( Base & base )
{
   dynamic_cast<Derived&>(base);
}

int main() {
   Base b;
   Derived d;
   Derived2 d2;
   ReDerived rd;

   test( b );   // throw: b is not a Derived object
   test( d );   // ok
   test( d2 );  // throw: d2 is not a Derived object
   test( rd );  // ok: rd is a ReDerived, and thus a derived object
}

उदाहरण में, कॉल testविभिन्न वस्तुओं को एक संदर्भ में बांधता है Base। आंतरिक रूप से संदर्भ को एक प्रकार से संदर्भ में डाउनकास्ट किया जाता है Derived: डाउनकास्ट केवल उन मामलों के लिए सफल होगा जहां संदर्भित ऑब्जेक्ट वास्तव में एक उदाहरण है Derived


2
मुझे लगता है कि यह स्पष्ट करना बेहतर है कि ऊपर साझा किए गए उदाहरण मान्यताओं के आधार पर काम करेंगे यदि कक्षाएं केवल बहुरूपी हैं यानी कम से कम बेस क्लास में कम से कम एक आभासी तरीका है।
इरिस

1
यह विफल हो जाएगा क्योंकि कक्षाएं बहुरूपी प्रकार नहीं हैं।
username_4567

4

dynamic_castटाइप चेकिंग के मामले में C ++ से आपको जो प्राप्त होता है, वह वास्तव में करीब नहीं है, लेकिन शायद यह आपको इसका उद्देश्य थोड़ा बेहतर समझने में मदद करेगा:

struct Animal // Would be a base class in C++
{
    enum Type { Dog, Cat };
    Type type;
};

Animal * make_dog()
{
   Animal * dog = new Animal;
   dog->type = Animal::Dog;
   return dog;
}
Animal * make_cat()
{
   Animal * cat = new Animal;
   cat->type = Animal::Cat;
   return cat;
}

Animal * dyn_cast(AnimalType type, Animal * animal)
{
    if(animal->type == type)
        return animal;
    return 0;
}

void bark(Animal * dog)
{
    assert(dog->type == Animal::Dog);

    // make "dog" bark
}

int main()
{
    Animal * animal;
    if(rand() % 2)
        animal = make_dog();
    else
        animal = make_cat();

    // At this point we have no idea what kind of animal we have
    // so we use dyn_cast to see if it's a dog

    if(dyn_cast(Animal::Dog, animal))
    {
        bark(animal); // we are sure the call is safe
    }

    delete animal;
}

3

एक dynamic_castएक प्रकार का उपयोग कर जाँच करता है RTTI । अगर यह विफल रहता है तो यह आपको एक अपवाद (यदि आपने इसे एक संदर्भ दिया है) या NULL अगर आप इसे एक पॉइंटर दिया है फेंक देंगे।


2

सबसे पहले, सी शब्दों में डायनामिक कास्ट का वर्णन करने के लिए, हमें सी कक्षाओं में प्रतिनिधित्व करना होगा। वर्चुअल फ़ंक्शंस के साथ वर्चुअल फ़ंक्शंस के लिए पॉइंटर्स के "VTABLE" का उपयोग करें। टिप्पणियाँ C ++ हैं। सुधार के लिए स्वतंत्र महसूस करें और संकलन त्रुटियों को ठीक करें ...

// class A { public: int data; virtual int GetData(){return data;} };
typedef struct A { void**vtable; int data;} A;
int AGetData(A*this){ return this->data; }
void * Avtable[] = { (void*)AGetData };
A * newA() { A*res = malloc(sizeof(A)); res->vtable = Avtable; return res; }

// class B : public class A { public: int moredata; virtual int GetData(){return data+1;} }
typedef struct B { void**vtable; int data; int moredata; } B;
int BGetData(B*this){ return this->data + 1; }
void * Bvtable[] = { (void*)BGetData };
B * newB() { B*res = malloc(sizeof(B)); res->vtable = Bvtable; return res; }

// int temp = ptr->GetData();
int temp = ((int(*)())ptr->vtable[0])();

फिर एक गतिशील कलाकार कुछ इस तरह है:

// A * ptr = new B();
A * ptr = (A*) newB();
// B * aB = dynamic_cast<B>(ptr);
B * aB = ( ptr->vtable == Bvtable ? (B*) aB : (B*) 0 );

1
प्रारंभिक प्रश्न था "क्या हम C में C ++ के डायनेमिक_कास्ट के बराबर लिख सकते हैं"।
डेविड रेना

1

C में कोई वर्ग नहीं हैं, इसलिए उस भाषा में डायनामिक_कास्ट लिखना असंभव है। सी संरचनाओं में विधियां नहीं हैं (परिणामस्वरूप, उनके पास आभासी विधियां नहीं हैं), इसलिए इसमें "गतिशील" कुछ भी नहीं है।


1

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

संपादित करें

यहां एक कार्यान्वयन है जो एक तकनीक को प्रदर्शित करता है। मैं दावा नहीं कर रहा हूँ कि संकलक इस तरह से कुछ भी उपयोग करता है, लेकिन मुझे लगता है कि यह अवधारणाओं को प्रदर्शित करता है:

class SafeCastableBase
{
public:
    typedef long TypeID;
    static TypeID s_nextTypeID;
    static TypeID GetNextTypeID()
    {
        return s_nextTypeID++;
    }
    static TypeID GetTypeID()
    {
        return 0;
    }
    virtual bool CanCastTo(TypeID id)
    {
        if (GetTypeID() != id) { return false; }
        return true;
    }
    template <class Target>
    static Target *SafeCast(SafeCastableBase *pSource)
    {
        if (pSource->CanCastTo(Target::GetTypeID()))
        {
            return (Target*)pSource;
        }
        return NULL;
    }
};
SafeCastableBase::TypeID SafeCastableBase::s_nextTypeID = 1;

class TypeIDInitializer
{
public:
    TypeIDInitializer(SafeCastableBase::TypeID *pTypeID)
    {
        *pTypeID = SafeCastableBase::GetNextTypeID();
    }
};

class ChildCastable : public SafeCastableBase
{
public:
    static TypeID s_typeID;
    static TypeID GetTypeID()
    {
        return s_typeID;
    }
    virtual bool CanCastTo(TypeID id)
    {
        if (GetTypeID() != id) { return SafeCastableBase::CanCastTo(id); }
        return true;
    }
};
SafeCastableBase::TypeID ChildCastable::s_typeID;

TypeIDInitializer ChildCastableInitializer(&ChildCastable::s_typeID);

class PeerChildCastable : public SafeCastableBase
{
public:
    static TypeID s_typeID;
    static TypeID GetTypeID()
    {
        return s_typeID;
    }
    virtual bool CanCastTo(TypeID id)
    {
        if (GetTypeID() != id) { return SafeCastableBase::CanCastTo(id); }
        return true;
    }
};
SafeCastableBase::TypeID PeerChildCastable::s_typeID;

TypeIDInitializer PeerChildCastableInitializer(&PeerChildCastable::s_typeID);

int _tmain(int argc, _TCHAR* argv[])
{
    ChildCastable *pChild = new ChildCastable();
    SafeCastableBase *pBase = new SafeCastableBase();
    PeerChildCastable *pPeerChild = new PeerChildCastable();
    ChildCastable *pSameChild = SafeCastableBase::SafeCast<ChildCastable>(pChild);
    SafeCastableBase *pBaseToChild = SafeCastableBase::SafeCast<SafeCastableBase>(pChild);
    ChildCastable *pNullDownCast = SafeCastableBase::SafeCast<ChildCastable>(pBase);
    SafeCastableBase *pBaseToPeerChild = SafeCastableBase::SafeCast<SafeCastableBase>(pPeerChild);
    ChildCastable *pNullCrossCast = SafeCastableBase::SafeCast<ChildCastable>(pPeerChild);
    return 0;
}

1

डायनेमिक_कास्ट RTTI का उपयोग करता है। यह आपके एप्लिकेशन को धीमा कर सकता है, आप RTTI http://arturx64.github.io/programming-world/2016/02/06/lazy-visitor.html के बिना डाउनकास्टिंग प्राप्त करने के लिए विज़िटर डिज़ाइन पैटर्न के संशोधन का उपयोग कर सकते हैं।


0

static_cast< Type* >(ptr)

C ++ में static_cast का उपयोग उन परिदृश्यों में किया जा सकता है जहां सभी प्रकार की कास्टिंग को संकलन समय पर सत्यापित किया जा सकता है

dynamic_cast< Type* >(ptr)

डायनेमिक_कास्ट C ++ में टाइप सेफ डाउन कास्टिंग करने के लिए इस्तेमाल किया जा सकता है । डायनेमिक_कास्ट टाइम पॉलीमॉर्फिज्म है। डायनेमिक_कास्ट ऑपरेटर, जो एक पॉइंटर प्रकार (या संदर्भ) से एक बेस टाइप को एक पॉइंटर (या संदर्भ) से एक व्युत्पन्न प्रकार में सुरक्षित रूप से परिवर्तित करता है।

उदाहरण 1:

#include <iostream>
using namespace std;

class A
{
public:
    virtual void f(){cout << "A::f()" << endl;}
};

class B : public A
{
public:
    void f(){cout << "B::f()" << endl;}
};

int main()
{
    A a;
    B b;
    a.f();        // A::f()
    b.f();        // B::f()

    A *pA = &a;   
    B *pB = &b;   
    pA->f();      // A::f()
    pB->f();      // B::f()

    pA = &b;
    // pB = &a;      // not allowed
    pB = dynamic_cast<B*>(&a); // allowed but it returns NULL

    return 0;
}

अधिक जानकारी के लिए यहां क्लिक करें

उदाहरण 2:

#include <iostream>

using namespace std;

class A {
public:
    virtual void print()const {cout << " A\n";}
};

class B {
public:
    virtual void print()const {cout << " B\n";}
};

class C: public A, public B {
public:
    void print()const {cout << " C\n";}
};


int main()
{

    A* a = new A;
    B* b = new B;
    C* c = new C;

    a -> print(); b -> print(); c -> print();
    b = dynamic_cast< B*>(a);  //fails
    if (b)  
       b -> print();  
    else 
       cout << "no B\n";
    a = c;
    a -> print(); //C prints
    b = dynamic_cast< B*>(a);  //succeeds
    if (b)
       b -> print();  
    else 
       cout << "no B\n";
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.