Question

Je suis tout à fait confondu avec le mot-clé dynamic_cast en C ++.

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
}

la définition dit:

  

Le mot-clé dynamic_cast jette une donnée d'un pointeur ou une référence       type à un autre, d'effectuer un contrôle d'exécution pour assurer la validité de la distribution

Peut-on écrire un équivalent de dynamic_cast de C ++ en C pour que je puisse mieux comprendre les choses?

Était-ce utile?

La solution

Voici un aperçu sur static_cast<> et dynamic_cast<> particulièrement en ce qui a trait aux pointeurs. Ceci est juste une diminution des effectifs de 101 niveau, il ne couvre pas toutes les subtilités.

static_cast (PTR)

Cela prend le pointeur dans ptr et tente de jeter en toute sécurité à un pointeur de type Type*. Cette fonte se fait au moment de la compilation. Il ne s'effectuer la conversion des types de type sont liés. Si les types ne sont pas liés, vous obtiendrez une erreur de compilation. Par exemple:

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;
}

dynamic_cast (PTR)

essaie à nouveau de prendre le pointeur dans ptr et en toute sécurité à jeter un pointeur de type Type*. Mais cette distribution est exécuté lors de l'exécution, pas le temps de compilation. Parce que c'est un casting d'exécution, il est particulièrement utile lorsqu'il est combiné avec des classes polymorphes. En fait, dans les cas certian les classes doivent être polymorphes pour que la distribution soit légal.

moulages peuvent aller dans une des deux directions: à partir de la base de dérivé (B2D) ou de dérivé de base (D2B). Il est assez simple de voir comment D2B moulages travailleraient à l'exécution. Soit ptr a été dérivé de Type ou il n'a pas été. Dans le cas de D2B dynamic_cast <> s, les règles sont simples. Vous pouvez essayer de jeter quoi que ce soit à toute autre chose, et si ptr était en fait dérivé de Type, vous obtiendrez un pointeur Type* retour de dynamic_cast. Sinon, vous obtiendrez un pointeur NULL.

Mais moulages B2D sont un peu plus compliqué. Considérez le code suivant:

#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() ne peut pas dire quel genre de CreateRandom() objet sera de retour, de sorte que le Bar* bar = (Bar*)base; coulé style C est décidément pas de type sécurisé. Comment pouvez-vous résoudre ce problème? Un moyen serait d'ajouter une fonction comme bool AreYouABar() const = 0; à la classe de base et de retour true de Bar et false de Foo. Mais il y a une autre façon: l'utilisation 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;

}

Les moulages exécutent à l'exécution, et le travail en interrogeant l'objet (pas besoin de vous soucier de la façon dont pour l'instant), en lui demandant si elle le type que nous recherchons. Si elle est, dynamic_cast<Type*> renvoie un pointeur; sinon il retourne NULL.

Pour que cette base de dérivés coulée au travail en utilisant dynamic_cast<>, Base, Foo et Bar doivent être ce que les appels standard types polymorphes . Pour être un type polymorphes, votre classe doit avoir au moins une fonction virtual. Si vos classes ne sont pas les types polymorphes, la base à l'utilisation de dérivés dynamic_cast ne compilera pas. Exemple:

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


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

    return 0;
}

Ajout d'une fonction virtuelle à la base, tel qu'un dtor virtuel, fera à la fois de base et Der types polymorphes:

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


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

    return 0;
}

Autres conseils

Sauf si vous implémentez votre propre RTTI (et sans passer par le système un) roulées à la main, il est impossible de mettre en œuvre dynamic_cast directement en C ++ code de niveau utilisateur. dynamic_cast est très lié au système RTTI de mise en œuvre du C.

Mais, pour vous aider à comprendre RTTI (et donc dynamic_cast) plus, vous devriez lire sur l'en-tête de <typeinfo>, et l'opérateur typeid. Cela renvoie les informations de type correspondant à l'objet que vous avez à portée de main, et vous pouvez demander divers (limité) les choses de ces objets d'information de type.

Plus de code en C, je pense qu'une définition anglaise pourrait être suffisant:

Compte tenu d'une base de classe dont il est une classe dérivée dérivée, dynamic_cast convertit un pointeur de base à un pointeur dérivé si et seulement si l'objet réel pointé est en fait un objet dérivé.

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
}

Dans l'exemple, l'appel à des objets différents test lie à une référence à Base. En interne, la référence est downcasted à une référence à Derived de manière typesafe:. Le baissés ne réussira que dans les cas où l'objet référencé est en effet une instance de Derived

Ce qui suit est pas vraiment à ce que vous obtenez de dynamic_cast de C ++ en termes de vérification de type, mais peut-être vous aidera à comprendre son but un peu mieux:

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;
}

A dynamic_cast effectue une vérification de type en utilisant RTTI . Si elle échoue, elle va vous lancer une exception (si vous avez donné une référence) ou NULL si vous avez donné un pointeur.

Tout d'abord, pour décrire casting dynamique en termes C, nous devons représenter les classes en C. Les classes avec des fonctions virtuelles utilisent un « VTABLE » de pointeurs vers les fonctions virtuelles. Les commentaires sont C ++. Ne hésitez pas à reformater et corriger des erreurs de compilation ...

// 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])();

Ensuite, un casting dynamique est quelque chose comme:

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

Il n'y a pas de classes en C, il est donc impossible d'écrire à dynamic_cast dans cette langue. structures C ne sont pas des méthodes (par conséquent, ils ne sont pas des méthodes virtuelles), donc il n'y a rien dans ce « dynamique ».

Non, pas facilement. Le compilateur attribue une identité unique à chaque classe, cette information est référencée par chaque instance d'objet, et qui est ce qui est inspecté lors de l'exécution pour déterminer si une distribution dynamique est légal. Vous pouvez créer une classe de base standard avec ces informations et les opérateurs pour faire l'inspection d'exécution sur cette classe de base, toute classe dérivée informerait la classe de base de sa place dans la hiérarchie des classes et toutes les instances de ces classes serait runtime-coulable via vos opérations.

modifier

Voici une implémentation qui démontre une technique. Je ne prétends pas que le compilateur utilise quelque chose comme ça, mais je pense que cela démontre les concepts:

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;
}

dynamic_cast utilise RTTI. Il peut ralentir votre application, vous pouvez utiliser la modification du modèle de conception des visiteurs pour atteindre downcasting sans RTTI http://arturx64.github.io/programming-world/2016/02/06/lazy-visitor.html

static_cast< Type* >(ptr)

static_cast en C ++ peut être utilisé dans des scénarios où tous les types coulée peut être vérifiée au moment de la compilation .

dynamic_cast< Type* >(ptr)

dynamic_cast en C ++ peut être utilisé pour effectuer SÛR type bas casting . dynamic_cast est exécuté polymorphisme de temps. L'opérateur dynamic_cast, qui convertit en toute sécurité à partir d'un pointeur (ou de référence) à un type de base à un pointeur (ou de référence) à un type dérivé.

par exemple 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;
}

Pour plus d'informations cliquez ici

par exemple 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";
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top