c++relpret_cast、virtual和templates都可以

c++ reinterpret_cast, virtual, and templates ok?

本文关键字:templates 都可以 virtual cast c++relpret      更新时间:2023-10-16

在C++中,假设以下类层次结构:

class BaseClass { };
class ChildClass : public BaseClass { };

进一步假设这两个类的工厂类具有一个通用的模板基类:

template<typename T>
class Factory {
public:
  virtual T* create() = 0;
};
class BaseClassFactory : public Factory<BaseClass> {
public:
  virtual BaseClass* create() {
    return new BaseClass(&m_field);
  }
private:
  SomeClass m_field;
};
class ChildClassFactory : public Factory<ChildClass> {
public:
  virtual ChildClass* create() {
    return new ChildClass(&m_field);
  }
private:
  SomeOtherClass m_field; // Different class than SomeClass
};

注意,ChildClassFactoryBaseClassFactory的大小/内部结构由于它们的字段不同而不同。

现在,如果有一个ChildClassFactory(或Factory<ChildClass>)的实例,我可以安全地将其强制转换为Factory<BaseClass>(通过reinterpret_cast)吗?

Factory<ChildClass>* childFactory = new ChildClassFactory();
// static_cast doesn't work - need to use reinterpret_cast
Factory<BaseClass>* baseFactory = reinterpret_cast<Factory<BaseClass>*>(childFactory);
// Does this work correctly? (i.e. is "cls" of type "ChildClass"?)
BaseClass* cls = baseFactory->create();

我知道不能总是以这种方式强制转换模板类,但在这种特殊情况下,强制转换应该是安全的,不是吗?

我已经用Visual C++2010对它进行了测试,它确实有效。我现在的问题是,这是否可以移植到其他编译器?

更新:由于出现了一些混乱,让我进一步澄清一下在我的示例中什么(应该)是重要的:

  • ChildClassBaseClass的一个子类
  • Factory<BaseClass>的用户不知道将创建BaseClass的哪个子类。他只知道BaseClass是被创造出来的
  • Factory<T>没有自己的字段(除了vtable)
  • Factory::create()virtual

不,不是。除了抛出内容之外,您可能不会使用reinterpret_cast的结果,除非有一些特殊情况:

ISO14882:2011(e)5.2.10-7:

对象指针可以显式转换为不同的类型。70当类型为"pointer to T1"的prvalue v为转换为类型"指向cv T2的指针",如果T1和T2都是标准布局,则结果为static_cast类型(3.9),T2的对准要求不严格于T1的那些,或者如果任一类型是空的。转换类型的prvalue"指针到T1"到类型"指针到T2"(其中T1和T2是对象类型,其中T2的对齐要求为否比T1的那些更严格)并且回到其原始类型产生原始指针值。任何其他此类指针的结果转换未指定。

为了使可能的失败场景更加清楚,可以考虑多重继承,其中使用static_castdynamic_cast有时会调整指针值,但reinterpret_cast不会。考虑在本例中从A*转换为B*:

struct A { int x; };
struct B { int y; };
struct C : A, B { };

要了解代码是如何以不同的方式失败的,请考虑大多数编译器是如何实现虚拟函数调用机制的:使用虚拟指针。ChildClassFactory的实例将有一个虚拟指针,指向ChildClassFactory的虚拟表。现在,当你reinterpret_cast这个野兽时,它只是碰巧"工作"了,因为编译器需要一些虚拟指针,指向一个布局相同/相似的虚拟表。但它仍将包含指向ChildCLassFactory虚拟函数的值,因此这些函数将被调用。在调用未定义的行为之后,所有这些都很长。就好像你开车跳进一个大峡谷,只因为你还没有落地就想"嘿,一切都很好"。

否,interpret_cast仅用于低级代码,因为它不会执行正确的地址操作。请改用static_cast或dynamic_cast,

你为什么想要两个工厂?这不符合GoF工厂的模式。

interpret_cast不是这样做的方法,因为它很慢(运行时检查),并且不是一个很好的OO设计(您希望在语言中使用polymophism构建)。

相反,在生成所追求的类型的工厂类中生成构造函数,然后让这些构造函数调用各个类型的构造函数。

工厂模式允许您对实现中的更改一无所知,这是一件好事,因为您可以最大限度地减少依赖关系,并允许在未来更容易地维护代码。

我勾选了上面的原始答案(为了表扬他),但我想我应该总结一下我在这里学到的东西。

因此,基本问题是没有定义必须如何实现调度虚拟呼叫。

这意味着,内部用于虚拟呼叫调度的数据结构(例如vtables)在从同一模板创建的模板实例化之间可能是位兼容的,也可能是不兼容的。

相关文章: