C ++安全替代dynamic_cast

时间:2012-11-16 13:27:14

标签: c++ casting

在什么情况下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()”我曾经决定是否可以转换它们。为了这样的目的,为了避免使用dynamic_casts,完全使用reinterpret_casts是完全错误的吗?(即它是一个偏执的设计,本质上是危险的,而且不够灵活,可能会引入不必要的麻烦?执行普通的动态转换可能会更安全并且值得花费较小的性能吗?我知道多继承和/或虚拟继承会弄乱任何其他的转换操作,除了多态/动态之外)。

6 个答案:

答案 0 :(得分:6)

您无法安全地使用reinterpret_cast向下转发。但你可以使用

  • static_cast,当您知道对象的动态类型(可能来自)您投下的对象时,

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

另一方面,对于 upcast ,您可以(但不应该)使用C样式转换以转换为无法访问的基础。它在标准中得到特别支持。但我从来没有找到过使用它的机会。

答案 1 :(得分:4)

只回答你的第一句话:永远不会。

静态地转换指向更多派生指针的基指针的唯一有效方法是使用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的目的而使用char指针。)

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

答案 2 :(得分:1)

你应该避免在可能的情况下进行reinterpret_cast,使用reinterpret_cast的主要原因是当你处理用C编写的遗留代码时,否则应该首选static_cast和dynamic_cast,如果你的设计要求你使用reinterpret_cast你可能想要这个作为暗示您的设计可能不是最佳的。

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

答案 3 :(得分:0)

为什么在这种情况下避免使用dynamic_cast?通过在进行演员表之前调用虚拟成员函数,您可能比使用dynamic_cast支付更多(在性能方面)。

答案 4 :(得分:0)

对不是B的东西调用B方法函数是危险的。该问题的标题是“安全”替代,您的代码不安全。

大多数情况下,使用dynamic_cast是不好的,也是设计不佳的标志。

有时它很有用,特别是在使用版本化插件时。您加载一个插件并获取一个可能支持或不支持新功能的对象,并且您可以动态地将您知道它支持的接口(基类)转换为更高版本(从它派生的)。如果它有效,您可以使用新功能,如果不是,您必须禁用此功能或使用较旧的处理方式。

有时您可以使用双重调度来执行此类操作。

答案 5 :(得分:0)

我知道dynamic_cast的唯一“安全”替代方法是使用Visitor Pattern。示例(Compiler Explorer):

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<<" A\n";
    }

    virtual int GetType() { return 1; }

    virtual void Accept(TypeVisitor& visitor) {
        visitor.VisitA(*this);
    }
};

class B: public A
{
public:
    void Hello() override
    {
        cout<< " B\n";
    }

    void Do()
    {
        cout << " Another method of B\n";
    }

    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