C++dynamic_cast的安全替代品

C++ safe alternative to dynamic_cast

本文关键字:安全 替代品 cast C++dynamic      更新时间:2023-10-16

在哪些情况下,reinterpret_cast可用于从实际上是派生实例指针的基本指针进行强制转换?(通过多态性)。

如果继承是多态的,则静态强制转换不起作用。

我考虑了这个微不足道的场景:

class A
{
public:
    virtual void Hello()
    {
        cout<<" A ";
    }
    virtual int GetType() { return 1; }
};
class B: public A
{
public:
    void Hello()
    {
        cout<< " B ";
    }
    void Do()
    {
        cout << " Another method of B";
    }
    int GetType() { return 2;}
};
/// ... sample/test code
A* a1 = new A();
A* b1 = new B();
A* a2;
B* b2;
if (a1->GetType() == 1)
{
    a2 = a1;
    a2->Hello();
}
else
if (a1->GetType() == 2)
{
    b2 = reinterpret_cast<B*>(a1);
    b2->Do();
    b2->Hello();
}

介意非常幼稚的"伪类型识别方法GetType())我曾经决定我是否可以转换它们。为了这种目的而使用reinterpret_casts来避免dynamic_casts,是完全错误的吗?(即,这是一种偏执的设计,本质上是危险的,而且不那么灵活,可能会带来不必要的麻烦吗?执行正常的动态转换是否更安全且值得花费少量的性能成本?我知道多重继承和/或虚拟继承会搞砸任何其他强制转换操作,除了多态/动态操作)。

您不能使用 reinterpret_cast 安全地向下投射。但是你可以使用

  • static_cast ,当您知道对象的动态类型是(可能派生自)您投射到的对象时,并且

  • dynamic_cast,引用或指针,如果静态已知的类是多态的。

在另一个方向上,对于向上投射,您可以(但不应该)使用 C 样式投射以投射到无法访问的基底。它在标准中特别支持。不过,我从来没有找到使用它的机会。

回答你的第一句话:从不。

静态地将基指针转换为派生指针的唯一有效方法是使用 static_cast ,并且仅在基指针是非虚拟时才有效:

Base * b = &derived;                       // implicit Derived * => Base *
Derived * p = static_cast<Derived *>(b);   // OK, I know what *b really is

应将静态强制转换视为隐式转换的对立面。

reinterpret_cast是完全错误的。(通常唯一可以接受的重新解释转换是用于 I/O 目的的字符指针。

(当你有一个指向虚拟基的指针时,你别无选择,只能使用dynamic_cast,但这当然是因为在这种情况下,基子对象仅在运行时确定。

你应该

尽可能避免做reinterpret_cast,使用reinterpret_cast的主要原因是当你处理用C编写的遗留代码时,否则static_cast和dynamic_cast应该是首选,如果你的设计要求你使用reinterpret_cast你可能想把它作为一个暗示,你的设计可能不是最佳的。

static_cast可用于多态类型,只要您确定它们总是会成功,否则您应该使用dynamic_cast。

在这种情况下,为什么要避免dynamic_cast?通过在执行强制转换之前必须调用虚拟成员函数,您可能会支付比使用 dynamic_cast 支付更多的费用(就性能而言)。

在不是 B 的东西上调用 B 方法函数是危险的。该问题的标题为"安全"替代方案,并且您的代码不安全。

大多数时候,使用dynamic_cast是不好的,是设计不佳的标志。

在某些情况下,它很有用,尤其是在使用版本化插件时。你加载一个插件并得到一个可能支持也可能不支持新功能的对象,你可以动态地将你知道它支持的接口(基类)强制转换为更高版本(从它派生)。如果它有效,您可以使用新功能,如果没有,则必须禁用此功能或使用较旧的执行方式。

有时您可以使用双重调度来做这种事情。

我知道dynamic_cast的唯一"安全"替代方案是使用访客模式。示例(编译器资源管理器):

class A;
class B;
class TypeVisitor {
 public:
    virtual void VisitA(A&) = 0;
    virtual void VisitB(B&) = 0;
    virtual ~TypeVisitor() = default;
};
class A
{
public:
    virtual void Hello()
    {
        cout<<" An";
    }
    virtual int GetType() { return 1; }
    virtual void Accept(TypeVisitor& visitor) {
        visitor.VisitA(*this);
    }
};
class B: public A
{
public:
    void Hello() override
    {
        cout<< " Bn";
    }
    void Do()
    {
        cout << " Another method of Bn";
    }
    int GetType() override { return 2; }
    void Accept(TypeVisitor& visitor) override {
        visitor.VisitB(*this);
    }
};
class PrintHelloVisitor : public TypeVisitor {
 public:
    virtual void VisitA(A& a) override {
        a.Hello();
    }
    virtual void VisitB(B& b)  override {
        b.Hello();
        b.Do(); // calling also the method that is not virtual
    }
    virtual ~PrintHelloVisitor() = default;
};
int main() {
    PrintHelloVisitor print_visitor;
    unique_ptr<A> a1 = make_unique<A>();
    a1->Accept(print_visitor);
    unique_ptr<A> b1 = make_unique<B>();
    b1->Accept(print_visitor);
    unique_ptr<B> b2 = make_unique<B>();
    b2->Accept(print_visitor);
}

指纹:

 A
 B
 Another method of B
 B
 Another method of B