C++ 中的 dynamic_cast 和 static_cast

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2253168/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 22:43:35  来源:igfitidea点击:

dynamic_cast and static_cast in C++

c++dynamic-cast

提问by Vijay

I am quite confused with the dynamic_castkeyword in C++.

dynamic_cast对 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
}

the definition says:

定义说:

The dynamic_castkeyword casts a datum from one pointer or reference type to another, performing a runtime check to ensure the validity of the cast

dynamic_cast关键字从一个指针或引用类型到另一个蒙上了基准,执行运行时检查以确保铸造的有效性

Can we write an equivalent of dynamic_castof C++ in C so that I could better understand things?

我们可以用dynamic_castC编写一个等效的C++ 以便我更好地理解事物吗?

回答by John Dibling

Here's a rundown on static_cast<>and dynamic_cast<>specifically as they pertain to pointers. This is just a 101-level rundown, it does not cover all the intricacies.

这是关于指针的简要说明static_cast<>dynamic_cast<>特别是与指针有关的内容。这只是一个 101 级的纲要,并没有涵盖所有的错综复杂。

static_cast< Type* >(ptr)

static_cast< 类型* >(ptr)

This takes the pointer in ptrand tries to safely cast it to a pointer of type Type*. This cast is done at compile time. It will only perform the cast if the type types are related. If the types are not related, you will get a compiler error. For example:

这将指针传入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;
}

dynamic_cast< Type* >(ptr)

dynamic_cast< 类型* >(ptr)

This again tries to take the pointer in ptrand safely cast it to a pointer of type Type*. But this cast is executed at runtime, not compile time. Because this is a run-time cast, it is useful especially when combined with polymorphic classes. In fact, in certian cases the classes mustbe polymorphic in order for the cast to be legal.

这再次尝试接收指针ptr并将其安全地转换为类型为 的指针Type*。但是这个转换是在运行时执行的,而不是编译时。因为这是一个运行时转换,所以它在与多态类结合时特别有用。事实上,在某些情况下,类必须是多态的,才能使转换合法。

Casts can go in one of two directions: from base to derived (B2D) or from derived to base (D2B). It's simple enough to see how D2B casts would work at runtime. Either ptrwas derived from Typeor it wasn't. In the case of D2B dynamic_cast<>s, the rules are simple. You can try to cast anything to anything else, and if ptrwas in fact derived from Type, you'll get a Type*pointer back from dynamic_cast. Otherwise, you'll get a NULL pointer.

转换可以在两个方向之一进行:从基础到派生 (B2D) 或从派生到基础 (D2B)。了解 D2B 转换在运行时的工作方式非常简单。要么ptr来自,Type要么不是。在 D2B dynamic_cast<> 的情况下,规则很简单。您可以尝试将任何内容转换为其他任何内容,如果ptr实际上是从 派生的Type,您Type*将从dynamic_cast. 否则,您将得到一个 NULL 指针。

But B2D casts are a little more complicated. Consider the following code:

但是 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()can't tell what kind of object CreateRandom()will return, so the C-style cast Bar* bar = (Bar*)base;is decidedly not type-safe. How could you fix this? One way would be to add a function like bool AreYouABar() const = 0;to the base class and return truefrom Barand falsefrom Foo. But there is another way: use dynamic_cast<>:

main()无法判断CreateRandom()将返回什么样的对象,因此 C 风格Bar* bar = (Bar*)base;的强制转换显然不是类型安全的。你怎么能解决这个问题?一种方法是将像 boolAreYouABar() const = 0;这样的函数添加到基类并trueBarfalsefrom返回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;

}

The casts execute at runtime, and work by querying the object (no need to worry about how for now), asking it if it the type we're looking for. If it is, dynamic_cast<Type*>returns a pointer; otherwise it returns NULL.

强制转换在运行时执行,并通过查询对象来工作(现在无需担心如何),询问它是否是我们正在寻找的类型。如果是,则dynamic_cast<Type*>返回一个指针;否则返回NULL。

In order for this base-to-derived casting to work using dynamic_cast<>, Base, Foo and Bar must be what the Standard calls polymorphic types. In order to be a polymorphic type, your class must have at least one virtualfunction. If your classes are not polymorphic types, the base-to-derived use of dynamic_castwill not compile. Example:

为了使这种从基到派生的转换能够使用dynamic_cast<>,Base、Foo 和 Bar 必须是标准所称的多态类型。为了成为一种多态类型,您的类必须至少具有一个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;
}

Adding a virtual function to base, such as a virtual dtor, will make both Base and Der polymorphic types:

向 base 添加一个虚函数,例如一个 virtual dtor,将使 Base 和 Der 都成为多态类型:

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


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

    return 0;
}

回答by Chris Jester-Young

Unless you're implementing your own hand-rolled RTTI (and bypassing the system one), it's not possible to implement dynamic_castdirectly in C++ user-level code. dynamic_castis very much tied into the C++ implementation's RTTI system.

除非您正在实现自己的手动 RTTI(并绕过系统 RTTI),否则不可能dynamic_cast直接在 C++ 用户级代码中实现。dynamic_cast与 C++ 实现的 RTTI 系统密切相关。

But, to help you understand RTTI (and thus dynamic_cast) more, you should read up on the <typeinfo>header, and the typeidoperator. This returns the type info corresponding to the object you have at hand, and you can inquire various (limited) things from these type info objects.

但是,为了帮助您更多地了解 RTTI(并因此dynamic_cast),您应该阅读<typeinfo>标题和typeid运算符。这将返回与您手头的对象对应的类型信息,您可以从这些类型信息对象中查询各种(有限的)事物。

回答by David Rodríguez - dribeas

More than code in C, I think that an english definition could be enough:

不仅仅是 C 中的代码,我认为一个英文定义就足够了:

Given a class Base of which there is a derived class Derived, dynamic_castwill convert a Base pointer to a Derived pointer if and only if the actual object pointed at is in fact a Derived object.

给定一个具有派生类 Derived 的类 Base,dynamic_cast当且仅当指向的实际对象实际上是 Derived 对象时,才会将 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
}

In the example, the call to testbinds different objects to a reference to Base. Internally the reference is downcastedto a reference to Derivedin a typesafe way: the downcast will succeed only for those cases where the referenced object is indeed an instance of Derived.

在示例中,对 的调用test将不同的对象绑定到对 的引用Base。在内部,引用以类型安全的方式向下转换为对的引用Derived:只有在被引用的对象确实是 的实例的情况下,向下转换才会成功Derived

回答by Manuel

The following is not really close to what you get from C++'s dynamic_castin terms of type checking but maybe it will help you understand its purpose a little bit better:

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

回答by f4.

A dynamic_castperforms a type checking using RTTI. If it fails it'll throw you an exception (if you gave it a reference) or NULL if you gave it a pointer.

Adynamic_cast使用RTTI执行类型检查。如果它失败,它会抛出一个异常(如果你给了它一个引用)或 NULL 如果你给了它一个指针。

回答by David Rayna

First, to describe dynamic cast in C terms, we have to represent classes in C. Classes with virtual functions use a "VTABLE" of pointers to the virtual functions. Comments are C++. Feel free to reformat and fix compile errors...

首先,为了用 C 术语描述动态转换,我们必须用 C 来表示类。具有虚函数的类使用指向虚函数的指针的“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])();

Then a dynamic cast is something like:

然后动态演员是这样的:

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

回答by arturx64

dynamic_cast uses RTTI. It can slow down your application, you can use modification of the visitor design pattern to achieve downcasting without RTTI http://arturx64.github.io/programming-world/2016/02/06/lazy-visitor.html

dynamic_cast 使用 RTTI。它可以减慢您的应用程序,您可以使用访问者设计模式的修改来实现没有 RTTI 的向下转换http://arturx64.github.io/programming-world/2016/02/06/lazy-visitor.html

回答by a1ex07

There are no classes in C, so it's impossible to to write dynamic_cast in that language. C structures don't have methods (as a result, they don't have virtual methods), so there is nothing "dynamic" in it.

C 中没有类,因此不可能用该语言编写 dynamic_cast。C 结构没有方法(因此,它们没有虚方法),因此其中没有任何“动态”。

回答by David Gladfelter

No, not easily. The compiler assigns a unique identity to every class, that information is referenced by every object instance, and that is what gets inspected at runtime to determine if a dynamic cast is legal. You could create a standard base class with this information and operators to do the runtime inspection on that base class, then any derived class would inform the base class of its place in the class hierarchy and any instances of those classes would be runtime-castable via your operations.

不,不容易。编译器为每个类分配一个唯一的标识,该信息被每个对象实例引用,这就是在运行时检查以确定动态转换是否合法的内容。您可以使用此信息和运算符创建一个标准基类来对该基类进行运行时检查,然后任何派生类都会通知基类它在类层次结构中的位置,并且这些类的任何实例都可以通过您的操作。

edit

编辑

Here's an implementation that demonstrates one technique. I'm not claiming the compiler uses anything like this, but I think it demonstrates the 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;
}

回答by Yogeesh H T

static_cast< Type* >(ptr)

static_cast< Type* >(ptr)

static_cast in C++ can be used in scenarios where all type casting can be verified at compile time.

C++ 中的 static_cast 可用于在编译时可以验证所有类型转换的场景。

dynamic_cast< Type* >(ptr)

dynamic_cast< Type* >(ptr)

dynamic_cast in C++ can be used to perform type safe down casting. dynamic_cast is run time polymorphism. The dynamic_cast operator, which safely converts from a pointer (or reference) to a base type to a pointer (or reference) to a derived type.

C++ 中的 dynamic_cast 可用于执行类型安全向下转换。dynamic_cast 是运行时多态性。dynamic_cast 运算符,它可以安全地从指向基类型的指针(或引用)转换为指向派生类型的指针(或引用)。

eg 1:

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

For more information clickhere

欲了解更多信息,请单击此处

eg 2:

例如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";
}