在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
};
请注意ChildClassFactory
和BaseClassFactory
的大小/内部结构因字段不同而不同。
现在,如果有一个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进行了测试,它确实有效。我现在的问题是这是否可以移植到其他编译器?
更新:由于存在一些混淆,让我在我的例子中澄清一些(应该是)重要的内容:
ChildClass
是BaseClass
Factory<BaseClass>
的用户不知道将创建BaseClass
的子类。他所知道的是BaseClass
已经创建。Factory<T>
没有自己的字段(vtable除外)。Factory::create()
是virtual
答案 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_cast
或dynamic_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)可能或可能在从同一模板创建的模板实例中与位相容。