C++dynamic_cast的安全替代品
C++ safe alternative to dynamic_cast
在哪些情况下,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
- 从不同线程使用int64的不同字节安全吗
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 虚拟决赛作为安全
- 获取日期异步信号安全吗?如果在信号处理程序中使用,它会导致死锁吗
- 如何将元素添加到数组的线程安全函数?
- C++中的线程安全删除
- 通过网络、跨平台传递std::变体是否安全
- 在std::thread中,joinable()然后join()线程安全吗
- 使用std::istream::peek()总是安全的吗
- 从值小于256的uint16到uint8的Endian安全转换
- C++中原子的替代品<variant>
- 在c++队列中使用pop和visit实现线程安全
- 在类型和包装器之间reinterpret_cast是否安全<Type>?
- 以线程安全的方式调用"QQuickPaintedItem::updateImage(const QImage&image)"(no QThread)
- 全局变量 多读取器 一个写入器多线程安全?
- 安全到标准:移动会员?
- AcquireCredentialsHandleA() 返回 PFX 文件的0x8009030e(安全包中没有可用的凭据
- 共享队列的线程安全
- C++dynamic_cast的安全替代品
- Microsoft是标准::reverse_copy的安全替代品?