C++ 中的 dynamic_cast 和 static_cast

2021-12-31 00:00:00 casting c++ dynamic-cast

我对 C++ 中的 dynamic_cast 关键字很困惑.

I am quite confused with the dynamic_cast keyword 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
}

定义说:

dynamic_cast 关键字从一个指针或引用转换数据类型到另一个,执行运行时检查以确保转换的有效性

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

我们能否用 C 编写 C++ 的 dynamic_cast 等价物,以便我更好地理解事物?

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

推荐答案

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

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.

这获取ptr 中的指针并尝试将其安全地转换为Type* 类型的指针.这个转换是在编译时完成的.如果类型相关,它只会执行强制转换.如果类型不相关,您将收到编译器错误.例如:

This takes the pointer in ptr and 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 types are related. If the types are not related, you will get a compiler error. For example:

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)

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

dynamic_cast< Type* >(ptr)

This again tries to take the pointer in ptr and 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 must be polymorphic in order for the cast to be legal.

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

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 ptr was derived from Type or 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 ptr was in fact derived from Type, you'll get a Type* pointer back from dynamic_cast. Otherwise, you'll get a NULL pointer.

但是 B2D 类型转换要复杂一些.考虑以下代码:

But B2D casts are a little more complicated. Consider the following 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() 无法判断 CreateRandom() 会返回什么样的对象,所以 C 风格的强制转换 Bar* bar = (Bar*)base; 显然不是类型安全的.你怎么能解决这个问题?一种方法是将类似 bool AreYouABar() const = 0; 的函数添加到基类,并从 Bar 返回 true>false 来自 Foo.但还有另一种方式:使用 dynamic_cast<>:

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 true from Bar and false from Foo. But there is another way: use 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 返回一个指针;否则返回NULL.

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<> 进行这种从基础到派生的转换,Base、Foo 和 Bar 必须是标准所称的多态类型.为了成为一种多态类型,你的类必须至少有一个 virtual 函数.如果您的类不是多态类型,dynamic_cast 的从基到派的使用将无法编译.示例:

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 virtual function. If your classes are not polymorphic types, the base-to-derived use of dynamic_cast will not compile. Example:

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


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

    return 0;
}

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

Adding a virtual function to base, such as a virtual dtor, will make both Base and Der polymorphic 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;
}

相关文章