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