c ++ reinterpret_cast,virtual和templates好吗?

时间:2012-01-26 13:30:34

标签: c++ templates casting virtual reinterpret-cast

在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

3 个答案:

答案 0 :(得分:5)

不,不是。除了一些特殊情况之外,您不能使用除reinterpret_cast之外的结果来重新投射东西:

ISO14882:2011(e)5.2.10-7:

  

可以将对象指针显式转换为对象指针   一个不同的类型.70当一个“指向T1的指针”类型的prvalue v是   转换为“指向cv T2的指针”类型,如果T1和T2都是标准布局,则结果为static_cast(static_cast(v))   类型(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虚函数的值,因此将调用这些函数。在调用未定义的行为之后,所有这些都是很长的。就好像你正驾着汽车跳进一个大峡谷,并且认为“嘿,一切都很好”只是因为你还没有到达地面。

答案 1 :(得分:0)

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

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

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

而是在工厂类中创建构造函数,生成您所使用的类型,然后让它们调用各个类型的构造函数。

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

答案 2 :(得分:0)

我已经勾选了上面的原始答案(给他信用),但我想我总结了我在这里学到的东西。

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

这意味着内部用于虚拟呼叫调度的数据结构(例如vtable)可能或可能在从同一模板创建的模板实例中与位相容。