2

在什么情况下可以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?(即它是一种偏执的设计,本质上是危险的并且不太灵活会引入不必要的麻烦?执行正常的动态转换是否更安全且值得付出较小的性能成本?我知道多重继承和/或虚拟继承会弄乱任何其他转换操作,除了多态/动态的)。

4

6 回答 6

6

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

  • static_cast,当您知道对象的动态类型是(可能派生自)您向下转换的对象时,并且

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

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

于 2012-11-16T13:33:03.527 回答
5

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

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

Base * b = &derived;                       // implicit Derived * => Base *

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

静态转换应被视为与隐式转换相反。

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

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

于 2012-11-16T13:31:39.000 回答
1

您应该尽可能避免使用 reinterpret_cast,使用 reinterpret_cast 的主要原因是当您处理用 C 编写的遗留代码时,否则应该首选 static_cast 和 dynamic_cast,如果您的设计需要使用 reinterpret_cast,您可能希望将此作为提示你的设计可能不是最优的。

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

于 2012-11-16T13:32:22.093 回答
0

dynamic_cast为什么在这种情况下避免 a ?通过在进行强制转换之前必须调用虚拟成员函数,您可能会比使用dynamic_cast.

于 2012-11-16T13:30:08.760 回答
0

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

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

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

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

于 2012-11-16T13:46:56.440 回答
0

我知道的唯一“安全”替代 dynamic_cast 是使用Visitor Pattern。示例(编译器资源管理器):

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
于 2020-04-01T21:16:57.540 回答