这样一个垂头丧气的人安全吗
Is such a downcast safe?
假设我们有以下代码:
#include <memory>
#include <vector>
struct BaseComponent
{
template <typename T>
T * as()
{
return static_cast<T*>(this);
}
virtual ~BaseComponent() {}
};
template <typename T>
struct Component : public BaseComponent
{
virtual ~Component() {}
};
struct PositionComponent : public Component<PositionComponent>
{
float x, y, z;
virtual ~PositionComponent() {}
};
int main()
{
std::vector<std::unique_ptr<BaseComponent>> mComponents;
mComponents.emplace_back(new PositionComponent);
auto *pos = mComponents[0]->as<PositionComponent>();
pos->x = 1337;
return 0;
}
在T*as()方法中,我应该使用static_cast还是dynamic_cast?转换会失败吗?我需要像这样dynamic_cast吗?
auto *ptr = dynamic_cast<T*>(this);
if(ptr == nullptr)
throw std::runtime_error("D'oh!");
return ptr;
在您的情况下,没有办法静态地判断this
是否是正确的类型。你可能想要的是一个CRTP(奇怪的重复模板模式):
template <class T>
struct BaseComponent
{
T* as()
{
return static_cast<T*>(this);
}
virtual ~BaseComponent() {}
};
template <typename T>
struct Component : public BaseComponent<T>
{
virtual ~Component() {}
};
struct PositionComponent : public Component<PositionComponent>
{
float x, y, z;
virtual ~PositionComponent() {}
};
你可以这样做:
auto x = yourBaseComponent.as();
并且静态地具有正确的子类型。
您提供的代码是正确的,格式良好,但通常强制转换是不安全的。如果实际的对象不是PositionComponent
,那么编译器会非常乐意地假设它是,并且您将导致未定义的行为。
如果用dynamic_cast
替换强制转换,那么编译器将生成代码,在运行时验证转换是否有效。
真正的问题是你为什么需要这个。原因是有的,但通常情况下,使用强制转换表明您的设计存在问题。重新考虑是否可以做得更好(即重新设计代码,这样就不需要显式转换类型)
由于您使用的是unique_ptr<BaseComponent>
,自然会有转换失败的时候:在向量中插入新数据和消耗该数据是在不相关的地方完成的,而且编译器无法强制执行。
以下是无效强制转换的示例:
struct AnotherComponent : public Component<AnotherComponent>
{
virtual ~AnotherComponent () {}
};
std::vector<std::unique_ptr<BaseComponent>> mComponents;
mComponents.emplace_back(new AnotherComponent);
// !!! This code compiles, but it is fundamentally broken !!!
auto *pos = mComponents[0]->as<PositionComponent>();
pos->x = 1337;
在这方面,使用dynamic_cast
将提供更好的保护,防止as<T>
函数的错误使用。请注意,错误的用法可能不是故意的:任何时候编译器都无法为您检查类型,并且您可能存在类型不匹配的情况,您应该更喜欢dynamic_cast<T>
这里有一个小演示来说明dynamic_cast
将如何为您提供一定程度的保护。
在转换从基类派生的多态对象时,应始终使用dynamic_cast
。
在mComponents[0]
不是PositionComponent
(或从中派生的类)的情况下,上述代码将失败。由于让mComponents
保存指向BaseComponent
的指针的全部目的是为了将PositionComponent
对象以外的其他对象放入向量中,所以我认为您需要注意特定的场景。
通常,当您使用dynamic_cast
(或者通常铸造从公共基类派生的对象)时,这是一种"臭味"。通常情况下,这意味着对象不应该保存在一个公共容器中,因为它们之间的关系不够密切。
- 从不同线程使用int64的不同字节安全吗
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 虚拟决赛作为安全
- 获取日期异步信号安全吗?如果在信号处理程序中使用,它会导致死锁吗
- 如何将元素添加到数组的线程安全函数?
- C++中的线程安全删除
- 通过网络、跨平台传递std::变体是否安全
- 在std::thread中,joinable()然后join()线程安全吗
- 使用std::istream::peek()总是安全的吗
- 从值小于256的uint16到uint8的Endian安全转换
- 在c++队列中使用pop和visit实现线程安全
- 在类型和包装器之间reinterpret_cast是否安全<Type>?
- 以线程安全的方式调用"QQuickPaintedItem::updateImage(const QImage&image)"(no QThread)
- 全局变量 多读取器 一个写入器多线程安全?
- 安全到标准:移动会员?
- AcquireCredentialsHandleA() 返回 PFX 文件的0x8009030e(安全包中没有可用的凭据
- 共享队列的线程安全
- boost::文件系统::recursive_directory_iterator多线程安全
- 跨 DLL 边界访问虚拟方法是否安全/可能?
- 这样一个垂头丧气的人安全吗