在生产代码中删除dynamic_cast是安全的吗?

Are dynamic_casts safe to remove in production code?

本文关键字:cast 安全 dynamic 代码 删除      更新时间:2023-10-16

dynamic_cast s比较慢,但是它们比static_cast s更安全(当然,当与对象层次结构一起使用时)。我的问题是,在我的调试代码中确保所有(动态)类型转换都是正确的之后,我有任何理由不将它们更改为static_cast s吗?

我计划用下面的构造来做这件事。(顺便说一句,你能想到一个比assert_cast更好的名字吗?也许是debug_cast ?)

#if defined(NDEBUG)
    template<typename T, typename U>
    T assert_cast(U other) {
        return static_cast<T>(other);
    }
#else
    template<typename T, typename U>
    T assert_cast(U other) {
        return dynamic_cast<T>(other);
    }
#endif

Edit: Boost已经有了这个:polymorphic_downcast。感谢PlasmaHH指出这一点。

不!dynamic_cast做的不仅仅是投射。它可以检查对象的运行时类型。但是,它也可以遍历编译器不知道但只有在运行时才知道的层次结构。static_cast不能这样做。

例如:

class A1
{ 
    virtual ~A1() {} 
};
class A2
{
    virtual ~A2() {} 
};
class B : public A1, public A2
{ };
A1 *a1 = new B;
A2 *a2 = dynamic_cast<A2*>(a1); //no static_cast!
A1 *x = ...;
if (B *b = dynamic_cast<B*>(x)) //no static_cast!
  /*...*/; 

您应该断言dynamic_cast成功:

template<typename T, typename U>
T *assert_cast(U *other) {
    T *t = dynamic_cast<T>(other);
    assert(t);
    return t;
}

替换dynamic_caststatic_cast的情况下,当你确定它们是等效的是相同的删除null检查指针,你确定总是非空。出于性能考虑,您可以这样做。

只要您测试了运行时因素/变量/输入的所有可能组合,就可以。正如在注释中提到的,这类似于删除断言。

语言中没有任何东西会使它本质上不安全,只要保证强制类型转换总是正确的。但它确实让人感觉不安全,因为你可能永远无法做出这样的保证。


更新

Konstantin已经证明,在处理多重继承时,这种技术只适用于在继承树1上/下的单个步骤。

struct A1 { virtual ~A1() {} };
struct A2 { virtual ~A2() {} };
struct A3 { virtual ~A3() {} };
struct B : A1, A2 {};
struct C : A1, A3, A2 {};
int main() {
    A1* a1 = (rand() < RAND_MAX / 2 ? (A1*)new B : (A1*)new C);
    A2* p1 = dynamic_cast<A2*>(a1);
    // ^ succeeds, but is a cross-cast
    // A2* p2 = static_cast<A2*>(a1);
    // ^ ill-formed
    A2* p3 = static_cast<A2*>(static_cast<B*>(a1));
    // ^ must chain, instead.
    // but p3 is invalid because we never
    //   checked that `dynamic_cast<B*>(a1)` is valid.. and it's not
    // Instead, let's expand the `dynamic_cast`s into a chain, too:
    A2* p3 = dynamic_cast<B*>(a1);
    A2* p4 = dynamic_cast<A2>*(a1);
    // ^ p3 fails, so we know that we cannot use `static_cast` here
}

因此,您可以将dynamic_cast s替换为static_cast s iff:

  • 每个dynamic_cast只执行一次单次升压;
  • 每个dynamic_cast已知总是成功的。

1实际上这是一个简化,例如,向下转换将适用于任何数量的步骤。但这是一个很好的经验法则。

之后,我在调试代码中确保所有(动态)强制转换都是正确的对,我有什么理由不把它们改成static_casts吗?

恕我直言,如果您100%确定所有 dynamic_cast<>都是正确的,那么没有理由不将它们更改为static_cast<>可以更改

我想这取决于项目。如果是核电站管理软件,我更喜欢安全,如果是3D游戏,我更喜欢性能。您永远无法确保所有dynamic_cast在生产代码中都是正确的。