Ik ben nogal in de war met het trefwoord dynamic_cast
in 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
}
de definitie zegt:
Het sleutelwoord
dynamic_cast
werpt een datum vanaf één pointer of referentie
typ naar een ander, voer een runtime-controle uit om de geldigheid van de cast te garanderen
Kunnen we een equivalent van dynamic_cast
van C++ in C schrijven zodat ik dingen beter kan begrijpen?
Antwoord 1, autoriteit 100%
Hier is een overzicht van static_cast<>
en dynamic_cast<>
, specifiek omdat ze betrekking hebben op pointers. Dit is slechts een overzicht van 101 niveaus, het behandelt niet alle fijne kneepjes.
static_cast< Typ* >(ptr)
Dit neemt de aanwijzer in ptr
en probeert deze veilig naar een aanwijzer van het type Type*
te casten. Deze cast wordt gedaan tijdens het compileren. Het zal de cast alleen uitvoeren als de typen gerelateerd zijn. Als de typen niet gerelateerd zijn, krijgt u een compilerfout. Bijvoorbeeld:
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< Typ* >(ptr)
Dit probeert opnieuw de aanwijzer in ptr
te nemen en deze veilig naar een aanwijzer van het type Type*
te casten. Maar deze cast wordt uitgevoerd tijdens runtime, niet tijdens compileren. Omdat dit een runtime-cast is, is het vooral handig in combinatie met polymorfe klassen. In bepaalde gevallen moeten de klassen moeten polymorf zijn om de cast legaal te laten zijn.
Casts kunnen in twee richtingen gaan: van basis naar afgeleid (B2D) of van afgeleid naar basis (D2B). Het is eenvoudig genoeg om te zien hoe D2B-casts tijdens runtime zouden werken. Ofwel ptr
is afgeleid van Type
of niet. In het geval van D2B dynamic_cast<>s zijn de regels eenvoudig. Je kunt proberen alles naar iets anders te casten, en als ptr
in feite is afgeleid van Type
, krijg je een Type*
-aanwijzer terug van dynamic_cast
. Anders krijg je een NULL-aanwijzer.
Maar B2D-casts zijn iets ingewikkelder. Overweeg de volgende code:
#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()
kan niet zeggen wat voor soort object CreateRandom()
zal terugkeren, dus de cast in C-stijl Bar* bar = (Bar*)base;
is beslist niet typeveilig. Hoe zou je dit kunnen oplossen? Een manier zou zijn om een functie zoals bool AreYouABar() const = 0;
toe te voegen aan de basisklasse en true
terug te geven van Bar
en false
van Foo
. Maar er is een andere manier: gebruik 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;
}
De casts worden uitgevoerd tijdens runtime en werken door het object te bevragen (u hoeft zich voorlopig geen zorgen te maken over hoe), en vragen of dit het type is waarnaar we op zoek zijn. Als dit het geval is, retourneert dynamic_cast<Type*>
een pointer; anders geeft het NULL terug.
Om ervoor te zorgen dat deze cast-naar-afgeleide casting werkt met dynamic_cast<>
, moeten Base, Foo en Bar zijn wat de standaard polymorfe typen noemt. Om een polymorf type te zijn, moet je klasse minstens één virtual
functie hebben. Als uw klassen geen polymorfe typen zijn, wordt het van basis naar afgeleide gebruik van dynamic_cast
niet gecompileerd. Voorbeeld:
class Base {};
class Der : public Base {};
int main()
{
Base* base = new Der;
Der* der = dynamic_cast<Der*>(base); // ERROR - Won't compile
return 0;
}
Door een virtuele functie aan base toe te voegen, zoals een virtuele dtor, worden zowel Base als Der polymorfe typen:
class Base
{
public:
virtual ~Base(){};
};
class Der : public Base {};
int main()
{
Base* base = new Der;
Der* der = dynamic_cast<Der*>(base); // OK
return 0;
}
Antwoord 2, autoriteit 6%
Tenzij u uw eigen handgerolde RTTI implementeert (en de systeemversie omzeilt), is het niet mogelijk om dynamic_cast
rechtstreeks in C++-code op gebruikersniveau te implementeren. dynamic_cast
is nauw verbonden met het RTTI-systeem van de C++-implementatie.
Maar om je te helpen RTTI (en dus dynamic_cast
) beter te begrijpen, moet je de kop <typeinfo>
en de typeid
-operator. Dit geeft de type-info terug die overeenkomt met het object dat je bij de hand hebt, en je kunt verschillende (beperkte) dingen opvragen bij deze type-info-objecten.
Antwoord 3, autoriteit 4%
Meer dan code in C, ik denk dat een Engelse definitie voldoende zou kunnen zijn:
Gegeven een klasse Base waarvan er een afgeleide klasse Derived is, zal dynamic_cast
een Base-pointer converteren naar een Derived pointer als en alleen als het werkelijke object waarnaar wordt verwezen in feite een afgeleid object is.
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
}
In het voorbeeld bindt de aanroep van test
verschillende objecten aan een verwijzing naar Base
. Intern is de verwijzing gedowncast naar een verwijzing naar Derived
op een typeveilige manier: de downcast zal alleen slagen in die gevallen waarin het object waarnaar wordt verwezen inderdaad een instantie is van Derived
.
Antwoord 4
Het volgende komt niet echt in de buurt van wat u krijgt van C++’s dynamic_cast
in termen van typecontrole, maar misschien zal het u helpen het doel ervan een beetje beter te begrijpen:
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;
}
Antwoord 5
Ten eerste, om dynamische cast in C-termen te beschrijven, moeten we klassen in C vertegenwoordigen.
Klassen met virtuele functies gebruiken een “VTABLE” van verwijzingen naar de virtuele functies.
Opmerkingen zijn C++. Voel je vrij om opnieuw te formatteren en compileerfouten op te lossen…
// 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])();
Dan is een dynamische cast zoiets als:
// A * ptr = new B();
A * ptr = (A*) newB();
// B * aB = dynamic_cast<B>(ptr);
B * aB = ( ptr->vtable == Bvtable ? (B*) aB : (B*) 0 );
Antwoord 6
Een dynamic_cast
voert een typecontrole uit met behulp van RTTI . Als het mislukt, krijg je een uitzondering (als je het een verwijzing hebt gegeven) of NULL als je het een aanwijzer hebt gegeven.
Antwoord 7
Er zijn geen klassen in C, dus het is onmogelijk om dynamic_cast in die taal te schrijven. C-structuren hebben geen methoden (als resultaat hebben ze geen virtuele methoden), dus er zit niets “dynamisch” in.
Antwoord 8
Nee, niet gemakkelijk. De compiler wijst een unieke identiteit toe aan elke klasse, naar die informatie wordt verwezen door elke objectinstantie, en dat is wat tijdens runtime wordt geïnspecteerd om te bepalen of een dynamische cast legaal is. Je zou een standaard basisklasse kunnen maken met deze informatie en operators om de runtime-inspectie op die basisklasse uit te voeren, dan zou elke afgeleide klasse de basisklasse informeren over zijn plaats in de klassenhiërarchie en alle instanties van die klassen zouden runtime-castable zijn via uw activiteiten.
bewerken
Hier is een implementatie die één techniek demonstreert. Ik beweer niet dat de compiler zoiets gebruikt, maar ik denk dat het de concepten demonstreert:
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;
}
Antwoord 9
static_cast< Type* >(ptr)
static_cast in C++ kan worden gebruikt in scenario’s waarin alle typecasting kan worden geverifieerd tijdens het compileren.
dynamic_cast< Type* >(ptr)
dynamic_cast in C++ kan worden gebruikt om type safe downcasting uit te voeren. dynamic_cast is runtime-polymorfisme. De operator dynamic_cast, die veilig converteert van een aanwijzer (of verwijzing) naar een basistype naar een aanwijzer (of verwijzing) naar een afgeleid type.
bijvoorbeeld 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;
}
Voor meer informatie klik hier
bijvoorbeeld 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";
}
Antwoord 10
dynamic_cast gebruikt RTTI. Het kan uw applicatie vertragen, u kunt een wijziging van het bezoekersontwerppatroon gebruiken om downcasting te bereiken zonder RTTI http://arturx64.github.io/programming-world/2016/02/06/lazy-visitor.html