简体   繁体   English

我应该在何时使用 dynamic_cast 进行向下转换?

[英]Should I use dynamic_cast whenever for downcast?

I noticed that compiler will optimize out some dynamic_cast when the following operation is non-polymorphic, for example the following code:我注意到当以下操作是非多态时,编译器会优化一些 dynamic_cast,例如以下代码:


#include <iostream>

using namespace std;

struct A
{
    virtual ~A() = default;
    virtual int f()
    {
        return 1;
    };
    virtual int g() = 0;
};

struct B : A
{
    int g() const
    {
        return 2;
    }
    int g() override
    {
        return 3;
    }
    int h()
    {
        return 4;
    }
};

int main()
{
    A*   p  = new B();
    auto u  = p->f();
    auto v1 = static_cast<B*>(p)->f();
    auto v2 = dynamic_cast<B*>(p)->f();
    auto w  = p->g();
    auto x1 = static_cast<const B*>(p)->g();
    auto x2 = dynamic_cast<B*>(p)->g();
    auto x3 = dynamic_cast<const B*>(p)->g();
    auto y  = dynamic_cast<B*>(p)->h();
    cout << u << v1 << v2 << w << x1 << x2 << x3 << y << endl;
    delete p;
    return 0;
}

There are only two dynamic_cast calls compiled with g++ -O2, that means that equals to static_cast, so should I always use dynamic_cast for downcast for no extra overhead to be considered?只有两个用 g++ -O2 编译的 dynamic_cast 调用,这意味着等于 static_cast,所以我是否应该始终使用 dynamic_cast 进行向下转换而不考虑额外的开销?

The main issue with dynamic_cast is that they are very slow and complicated. dynamic_cast的主要问题是它们非常缓慢和复杂。 Compilers can indeed optimize it when they are aware of the actual type in compile time but not always.当编译器知道编译时的实际类型时,编译器确实可以优化它,但并非总是如此。 Technically, your code should have 0 dynamic casts if compilers knew how to do it properly.从技术上讲,如果编译器知道如何正确执行,您的代码应该有 0 个动态转换。 So don't trust too much on the exact mechanisms of optimization that compilers use.所以不要太相信编译器使用的优化机制。

Certain parts of your code could've been optimized via abuse of undefined behavior.您的代码的某些部分可以通过滥用未定义的行为进行优化。 For example:例如:

dynamic_cast<B*>(p)->f(); 
// this is optimized instantly to p->f(); 
// if dynamic_cast returns nullptr it would be undefined behavior IIRC, 
// so optimizer can assume that the cast is successful and it becomes equivalent to
// static_cast<B*>(p)->f() after optimization,
// regardless of whether p is actually of type B or not

In general, it is difficult to provide concrete methodology on dynamic casts, if performance allows it, always dynamic cast on down casting.一般来说,很难提供关于动态转换的具体方法,如果性能允许,总是在向下转换上进行动态转换。 Not doing so is possible undefined behavior and security leak.不这样做可能是未定义的行为和安全漏洞。

If you have no guarantee that the derived class is of this specific type - you have no choice but to use dynamic cast.如果您不能保证派生的 class 是这种特定类型 - 您别无选择,只能使用动态转换。

If you have a guarantee but there might be bugs in the code, consider making a static assert with dynamic cast inside, and using static cast in optimized versions.如果您有保证但代码中可能存在错误,请考虑使用内部动态强制转换的 static 断言,并在优化版本中使用 static 强制转换。

In fact, I don't see any effective difference in using static_cast and dynamic_cast here.事实上,我在这里没有看到使用static_castdynamic_cast的任何有效区别。 First, if you call a virtual function via a casted pointer, all the following calls will have the very same effect — they will trigger dynamic dispatching :首先,如果您通过强制转换的指针调用虚拟 function ,那么以下所有调用都将产生相同的效果——它们将触发动态调度

auto v0 = p->f();
auto v1 = static_cast<B*>(p)->f();
auto v2 = dynamic_cast<B*>(p)->f();

dynamic_cast might add some overhead, but the observable effect will be the same. dynamic_cast可能会增加一些开销,但可观察到的效果是一样的。 Namely.即。 B::f will be called (if it's overridden). B::f将被调用(如果它被覆盖)。

As for non-virtual functions from a derived class, you need to cast:至于来自派生 class 的非虚拟函数,您需要强制转换:

auto x1 = static_cast<const B*>(p)->g();
auto x3 = dynamic_cast<const B*>(p)->g();

If those casts are invalid, you will get undefined behavior in both cases.如果这些强制转换无效,您将在这两种情况下得到未定义的行为。 If they are valid, there will be effectively the same.如果它们是有效的,那么实际上将是相同的。 Just again, with some additional overhead of dynamic_cast .再说一次,还有一些额外的dynamic_cast开销。

Note that, IMO, there is no dynamic_cast optimized away in your program.请注意,IMO,您的程序中没有dynamic_cast优化。 You can observe two dynamic_cast calls in your assembly, and you have two different forms of dynamic_cast in your code.您可以在程序集中观察到两个dynamic_cast调用,并且您的代码中有两个不同的dynamic_cast forms。 I assume a compiler is doing dynamic_cast<B*> only once and uses the result 3 times.我假设编译器只执行一次dynamic_cast<B*>并使用结果 3 次。


Note that you can bypass the dynamic dispatch by manually selecting which f you want to call by the following syntax:请注意,您可以通过以下语法手动选择要调用的f来绕过动态调度:

auto y1 = static_cast<B*>(p)->A::f();
auto y2 = static_cast<B*>(p)->B::f();

Or, with dynamic_cast the very same way.或者,以同样的方式使用dynamic_cast

Live demo: https://wandbox.org/permlink/CZRLPWxHjSMk8dFK .现场演示: https://wandbox.org/permlink/CZRLPWxHjSMk8dFK

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM