সি ++ এ ডায়নামিক_কাস্ট এবং স্ট্যাটিক_কাস্ট


155

আমি dynamic_castসি ++ এর কীওয়ার্ডটি নিয়ে বেশ বিভ্রান্ত ।

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_castসিতে ++ এর সমতুল্য লিখতে পারি যাতে আমি জিনিসগুলি আরও ভালভাবে বুঝতে পারি?


1
আপনি যদি dynamic_cast<>পর্দার আড়ালে কীভাবে কাজ করে (বা কতটা সি ++ কাজ করে) এর একটি ভাল ধারণা পেতে চান তবে একটি ভাল বই (এটি এত প্রযুক্তিগত কোনও কিছুর জন্য পড়াও বেশ সহজ) হ'ল লিপম্যানের "ইনসাইড দ্য সি ++ অবজেক্ট মডেল"। এছাড়াও স্ট্রস্ট্রুপের "ডিজাইন অ্যান্ড ইভোলিউশন অফ সি ++" এবং "দ্য সি ++ প্রোগ্রামিং ল্যাঙ্গুয়েজ" বইগুলি ভাল সংস্থান, তবে লিপম্যানের বইটি কীভাবে সি ++ 'পর্দার আড়ালে' কাজ করে তা উত্সর্গীকৃত।
মাইকেল বুড়

লাইনে মন্তব্যের B* b2 = dynamic_cast<B*> (ap) // 'b'অর্থ কী? b2 is pointer to bঅথবা কি?
এলআরডিপিআরডিএক্স

@ বোগদানসিকাচ কি প্রশ্ন? এর সহজ অর্থ হ'ল এপি এখন এক ধরণের বি শ্রেণি

উত্তর:


282

এখানে একটি রুনডাউন রয়েছে static_cast<>এবং dynamic_cast<>বিশেষত তারা পয়েন্টারগুলির সাথে সম্পর্কিত। এটি মাত্র 101-স্তরের রুনডাউন, এটি সমস্ত জটিলতা জুড়ে না।

স্ট্যাটিক_কাস্ট <প্রকার *> (পিটিআর)

এটি পয়েন্টারটি ভিতরে নিয়ে যায় ptrএবং এটিকে নিরাপদে টাইপের একটি পয়েন্টারে কাস্ট করার চেষ্টা করে Type*। এই castালাই সংকলন সময়ে করা হয়। প্রকারের প্রকারগুলি সম্পর্কিত হলে এটি কেবল theালাই সঞ্চালন করবে। প্রকারগুলি সম্পর্কিত না হলে আপনি একটি সংকলক ত্রুটি পাবেন। উদাহরণ স্বরূপ:

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*। তবে এই castালাই রানটাইম সময়ে কার্যকর করা হয়, সংকলনের সময় নয়। যেহেতু এটি একটি রান-টাইম castালাই, এটি বিশেষত পলিমারফিক ক্লাসগুলির সাথে মিলিত হয়ে কার্যকর হয়। প্রকৃতপক্ষে, সত্যিকারের ক্ষেত্রে কাস্টগুলি বৈধ হওয়ার জন্য ক্লাসগুলি অবশ্যই বহুকর্মী হতে হবে।

বর্ণগুলি দুটি দিকের একটিতে যেতে পারে: বেস থেকে প্রাপ্ত (বি 2 ডি) বা বেস থেকে প্রাপ্ত (ডি 2 বি) থেকে। এটি D2B কাস্টগুলি রানটাইমে কীভাবে কাজ করবে তা দেখার পক্ষে যথেষ্ট সহজ। হয় হয় ptrথেকে প্রাপ্ত হয়েছে Typeবা এটি ছিল না। D2B ডায়নামিক_কাস্ট <> এর ক্ষেত্রে, নিয়মগুলি সহজ। আপনি অন্য যে কোনও কিছুতে কাস্ট করার চেষ্টা করতে পারেন, এবং যদি ptrবাস্তবে উত্পন্ন হয় তবে Typeআপনি Type*পয়েন্টারটি ফিরে পাবেন dynamic_cast। অন্যথায়, আপনি একটি নাল পয়েন্টার পাবেন।

তবে বি 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;সিদ্ধান্ত নিয়ে টাইপ-নিরাপদ নয়। আপনি কিভাবে এটি ঠিক করতে পারেন? একটি উপায় হ'ল 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
সংকলকটি কেন এটি সম্পর্কে প্রথমে অভিযোগ করে? এবং যখন আমরা কেবলমাত্র বেসের জন্য কেবল ভার্চুয়াল ডেক্টর সরবরাহ করি না?
রিকা

5
এটা লক্ষনীয় যে যদি আপনি না Base* base = new Base;, dynamic_cast<Foo*>(base)হতে হবে NULL
Yay295

2
@ কোডারেক্স dyn ডায়নামিক_কাস্টের জন্য রান-টাইম টাইপ ইনফরমেশন (আরটিটিআই) দরকার যা কেবল পলিমারফিক, অর্থাৎ কমপক্ষে একটি ভার্চুয়াল পদ্ধতিযুক্ত ক্লাসগুলির জন্য উপলব্ধ।
এলভোরফিরিলমথ্রেডিয়া 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সি ++ প্রয়োগের আরটিটিআই সিস্টেমে খুব বেশি আবদ্ধ।

তবে, আরটিটিআই (এবং এইভাবে 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। অভ্যন্তরীণভাবে রেফারেন্স downcasted একটি রেফারেন্স করতে Derivedএকটি typesafe ভাবে: হতাশ কেবলমাত্র সেই ক্ষেত্রে যেখানে রেফারেন্সড বস্তুর প্রকৃতপক্ষে একটি দৃষ্টান্ত জন্য সফল হবে Derived


2
আমি মনে করি এটি স্পষ্ট করে বলা ভাল যে উপরের ভাগ করা উদাহরণগুলি অনুমানের উপর ভিত্তি করে কাজ করবে যদি ক্লাসগুলি পলিমারফিক হয় তবে কমপক্ষে বেস ক্লাসে কমপক্ষে একটি ভার্চুয়াল পদ্ধতি থাকে।
ইরশিস

1
এটি ব্যর্থ হবে কারণ ক্লাসগুলি পলিমারফিক ধরণের নয়।
ব্যবহারকারীর

4

dynamic_castটাইপ চেকিংয়ের ক্ষেত্রে নীচে আপনি সি ++ এর কাছ থেকে যা পেয়েছেন তার খুব কাছাকাছি নয় তবে সম্ভবত এটি আপনাকে এর উদ্দেশ্যটি আরও খানিকটা ভালভাবে বুঝতে সাহায্য করবে:

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 । যদি এটি ব্যর্থ হয় তবে এটি আপনাকে একটি ব্যতিক্রম (যদি আপনি এটি একটি রেফারেন্স দেন) ছুঁড়ে ফেলবে বা যদি আপনি এটি কোনও পয়েন্টার দেয় তবে NUL।


2

প্রথমে সি পদে গতিশীল castালাই বর্ণনা করার জন্য, আমাদের সিতে ক্লাস উপস্থাপন করতে হবে ভার্চুয়াল ফাংশন সহ ক্লাসগুলি ভার্চুয়াল ফাংশনগুলিতে পয়েন্টারগুলির একটি "VTABLE" ব্যবহার করে। মন্তব্যগুলি সি ++। সংকলন ত্রুটিগুলি পুনরায় ফর্ম্যাট এবং ঠিক করতে নির্দ্বিধায় ...

// 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 ++ এর ডায়নামিক_কাস্টের সমতুল্য লিখতে পারি"?
ডেভিড রায়না

1

সি তে কোনও ক্লাস নেই, সুতরাং সেই ভাষায় ডায়নামিক_কাস্ট লেখা অসম্ভব। সি স্ট্রাকচারের কোনও পদ্ধতি নেই (ফলস্বরূপ, তাদের ভার্চুয়াল পদ্ধতি নেই), সুতরাং এটিতে "গতিশীল" কিছুই নেই।


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

গতিশীল_কাস্ট আরটিটিআই ব্যবহার করে। এটা নিচে আপনার আবেদন মন্থর পারে, আপনি downcasting অর্জনে পরিদর্শক নকশা প্যাটার্ন পরিবর্তনের ব্যবহার করতে পারেন RTTI ছাড়া http://arturx64.github.io/programming-world/2016/02/06/lazy-visitor.html


0

static_cast< Type* >(ptr)

সি ++ এ স্ট্যাটিক_কাস্ট পরিস্থিতিগুলিতে ব্যবহার করা যেতে পারে যেখানে সংকলনের সময় সমস্ত প্রকারের ingালাই যাচাই করা যেতে পারে

dynamic_cast< Type* >(ptr)

সি ++ এ ডায়নামিক_কাস্ট টাইপ সেফ ডাউন কাস্টিং সঞ্চালনের জন্য ব্যবহার করা যেতে পারে । ডায়নামিক_কাস্ট রান টাইম পলিমারফিজম। ডায়নামিক_কাস্ট অপারেটর, যা একটি পয়েন্টার (বা রেফারেন্স) থেকে নিরাপদে একটি বেস টাইপকে একটি পয়েন্টার (বা রেফারেন্স) থেকে প্রাপ্ত একটি প্রকারভেদে নিরাপদে রূপান্তর করে।

যেমন 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";
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.