小编典典

C++ 中的 dynamic_cast 和 static_cast

all

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
}

定义说:

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

我们可以在 C 中编写相当于dynamic_castC++ 的内容,以便我更好地理解事物吗?


阅读 139

收藏
2022-08-24

共1个答案

小编典典

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

static_cast< 类型* >(ptr)

这会将指针放入ptr并尝试将其安全地转换为 type 的指针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< 类型* >(ptr)

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

强制转换可以沿两个方向之一进行:从基础到派生 (B2D) 或从派生到基础 (D2B)。看看 D2B
转换如何在运行时工作很简单。要么ptr来自,要么Type不是。对于 D2B
dynamic_cast<>s,规则很简单。您可以尝试将任何内容转换为其他任何内容,如果ptr实际上是从
派生的Type,您Type*将从dynamic_cast. 否则,您将得到一个 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;的强制转换绝对不是类型安全的。你怎么能解决这个问题?一种方法是AreYouABar() const = 0;在基类中添加一个类似
bool 的函数,然后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;

}

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

为了使这种从基到派生的转换能够使用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;
}

将虚函数添加到 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;
}
2022-08-24