无论何时我应该使用dynamic_cast进行下注吗?

Mid*_*umo 5 c++ dynamic-cast

我注意到,当以下操作非多态时,例如以下代码,编译器将优化一些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;
}

Run Code Online (Sandbox Code Playgroud)

只有两个用g ++ -O2编译的dynamic_cast调用,这意味着等于static_cast,因此我应该始终使用dynamic_cast进行向下转换,而不会考虑额外的开销吗?

ALX*_*23z 5

主要问题dynamic_cast是它们非常缓慢和复杂。编译器确实可以在编译时知道实际类型时对其进行优化,但并非总是如此。从技术上讲,如果编译器知道如何正确执行,您的代码应该有 0 个动态转换。所以不要太相信编译器使用的确切优化机制。

您的代码的某些部分可能已经通过滥用未定义的行为进行了优化。例如:

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
Run Code Online (Sandbox Code Playgroud)

一般来说,很难提供关于动态转换的具体方法,如果性能允许,总是动态转换向下转换。不这样做可能是未定义的行为和安全漏洞。

如果您不能保证派生类属于这种特定类型 - 您别无选择,只能使用动态转换。

如果您有保证但代码中可能存在错误,请考虑在内部使用动态转换创建静态断言,并在优化版本中使用静态转换。

  • @MidoriYakumo 我会说:_如果不需要,根本不要使用强制类型转换_。无论如何,“static_cast”和“dynamic_cast”都不会改变对象的动态类型。如果在强制转换的指针上调用虚函数,仍然会涉及虚函数调用机制。 (2认同)

Dan*_*ica 3

事实上,我在这里没有看到使用static_cast和有任何有效的区别dynamic_cast。首先,如果调用虚函数,则以下所有调用将具有完全相同的效果 \xe2\x80\x94 它们将触发动态调度

\n\n
auto v0 = p->f();\nauto v1 = static_cast<B*>(p)->f();\nauto v2 = dynamic_cast<B*>(p)->f();\n
Run Code Online (Sandbox Code Playgroud)\n\n

dynamic_cast可能会增加一些开销,但可观察到的效果是相同的。即。B::f将被调用(如果它被覆盖)。

\n\n

至于非虚函数派生类中的

\n\n
auto x1 = static_cast<const B*>(p)->g();\nauto x3 = dynamic_cast<const B*>(p)->g();\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果这些强制转换无效,那么在这两种情况下您都会得到未定义的行为。如果它们有效,则实际上是相同的。再说一遍,有一些额外的开销dynamic_cast

\n\n

dynamic_cast请注意,IMO,您的程序中没有优化。您可以dynamic_cast在程序集中观察到两个调用,并且dynamic_cast代码中有两种不同形式的 。我假设编译器正在做dynamic_cast<B*>一次并使用结果 3 次。

\n\n
\n\n

请注意,您可以通过手动选择哪个来绕过动态调度f请注意,您可以通过以下语法

\n\n
auto y1 = static_cast<B*>(p)->A::f();\nauto y2 = static_cast<B*>(p)->B::f();\n
Run Code Online (Sandbox Code Playgroud)\n\n

或者,与dynamic_cast同样的方式。

\n\n

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

\n