dynamic_cast和static_cast在C ++中
我很困惑与C ++中的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
关键字将一个数据从一个指针或引用types转换到另一个指针或引用types,执行运行时检查以确保投射的有效性
我们可以用C编写一个相当于C ++的dynamic_cast
,以便我能更好地理解事物吗?
以下是关于static_cast<>
和dynamic_cast<>
具体指针。 这只是一个101级的破败,并不能涵盖所有错综复杂的事情。
static_cast <Type *>(ptr)
这需要在ptr
的指针,并试图安全地将其转换为Type*
types的指针。 这个转换是在编译时完成的。 它只会在typestypes相关的情况下执行强制转换。 如果这些types不相关,你会得到一个编译器错误。 例如:
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 <types*>(ptr)
这又试图把指针放在ptr
并把它安全地转换成Type*
types的指针。 但是这个转换是在运行时执行的,而不是编译时间。 因为这是一个运行时间转换,所以在与多态类组合时特别有用。 事实上,在特殊情况下,类必须是多态才能使演员合法。
演员可以在两个方向中的一个:从基础派生(B2D)或从派生到基础(D2B)。 很容易看到D2B强制转换在运行时如何工作。 ptr
是从Type
派生的,或者它不是。 在D2B dynamic_cast <>的情况下,规则很简单。 您可以尝试将任何东西转换为其他任何东西,如果ptr
实际上是从Type
派生的,则会从dynamic_cast
返回一个Type*
指针。 否则,你会得到一个NULL指针。
但是B2D演员则更加复杂一些。 考虑下面的代码:
#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()
会返回什么样的对象,所以C风格强制转换Bar* bar = (Bar*)base;
绝对不是types安全的。 你怎么解决这个问题? 一种方法是添加一个像bool AreYouABar() const = 0;
到基类,从Bar
返回true
,从Foo
返回false
。 但还有另一种方法:使用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; }
这些强制转换在运行时执行,并通过查询对象来工作(现在不需要担心),询问它是否是我们正在寻找的types。 如果是, dynamic_cast<Type*>
返回一个指针; 否则返回NULL。
为了使这个基地派生的铸造使用dynamic_cast<>
,Base,Foo和Bar必须是标准调用多态types的东西 。 为了成为一个多态types,你的类必须至less有一个virtual
函数。 如果您的类不是多态types,那么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; }
添加一个虚拟函数到基地,如一个虚拟的dtor,将使基本和Der多态types:
class Base { public: virtual ~Base(){}; }; class Der : public Base {}; int main() { Base* base = new Der; Der* der = dynamic_cast<Der*>(base); // OK return 0; }
除非你正在实现自己的手动RTTI(并绕过系统之一),否则不可能直接在C ++用户级代码中实现dynamic_cast
。 dynamic_cast
与C ++实现的RTTI系统非常相关。
但是,为了更好地理解RTTI(以及dynamic_cast
),您应该阅读<typeinfo>
头文件和typeid
运算符。 这将返回与您手头上的对象相对应的types信息,您可以从这些types的信息对象查询各种(有限的)事物。
不仅仅是C语言的代码,我认为英文的定义可能就足够了:
给定一个派生类Derived的类Base,当且仅当指向的实际对象实际上是Derived对象时, dynamic_cast
才会将Base指针转换为Derived指针。
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
的引用。 在内部,引用被降级为以types安全的方式引用Derived
:只有被引用对象确实是Derived
实例的情况下,downcast才会成功。
dynamic_cast
使用RTTI执行types检查。 如果失败,它会抛出一个exception(如果你给它一个引用),或者如果你给它一个指针,则抛出NULL。
在types检查方面,下面的内容并不是真正接近C ++的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; }
C中没有类,所以用这种语言编写dynamic_cast是不可能的。 C结构没有方法(因此它们没有虚方法),所以没有什么“dynamic的”。
不,不容易。 编译器为每个类分配一个唯一的标识,每个对象实例都会引用这些信息,这就是在运行时检查以确定dynamic转换是合法的。 你可以用这个信息创build一个标准的基类,然后运算符在这个基类上进行运行时检查,然后任何派生类都会通知基类在类层次结构中的位置,这些类的任何实例都可以通过运行时转换你的操作。
编辑
这是一个演示一种技术的实现。 我没有声称编译器使用这样的东西,但我认为它演示的概念:
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使用RTTI。 它可以减慢你的应用程序,你可以使用访问者devise模式的修改,以实现downcasting没有RTTI http://arturx64.github.io/programming-world/2016/02/06/lazy-visitor.html
static_cast< Type* >(ptr)
C ++中的static_cast可用于编译时可以validation所有types转换的场景。
dynamic_cast< Type* >(ptr)
C ++中的dynamic_cast可以用来执行types安全的向下转换 。 dynamic_cast是运行时多态性。 dynamic_cast运算符,它安全地从指针(或引用)转换为基本types,转换为指向派生types的指针(或引用)。
例如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; af(); // A::f() bf(); // 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"; }