c++ reinterpret_cast、virtual 和 templates 可以吗?
在 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
的大小/内部结构为因领域不同而不同。
现在,如果 a 有一个 ChildClassFactory
(或 Factory
)的实例,我可以安全地将其转换为 Factory
(通过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
的子类code>Factory
的用户不知道BaseClass
将创建哪个子类。他只知道BaseClass
已创建。Factory
没有自己的字段(除了 vtable 之外)。Factory::create()
是虚拟
In C++, assume following class hierarchy:
class BaseClass { };
class ChildClass : public BaseClass { };
Further assume factory classes for these two classes with a common, templated base class:
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
};
Note that the size/internal structure of ChildClassFactory
and BaseClassFactory
is different due to their different fields.
Now, if a have an instance of ChildClassFactory
(or Factory<ChildClass>
), can I safely cast it to Factory<BaseClass>
(via 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();
I know that you can't always cast templated classes this way, but in this special case a cast should be safe, shouldn't it?
I've tested it with Visual C++ 2010 and it does work. My question now is whether this is portable to other compilers?
Update: Since there has been some confusion let me clarify some more what's (supposed to be) important in my example:
ChildClass
is a child class ofBaseClass
- A user of
Factory<BaseClass>
doesn't know what child class ofBaseClass
will be created. All he knows is thatBaseClass
is created. Factory<T>
has no fields of its own (other than the vtable).Factory::create()
isvirtual
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
不,事实并非如此。除了将内容强制转换回来之外,您不得使用
reinterpret_cast
的结果,但少数特殊情况除外:ISO14882:2011(e) 5.2.10-7:
为了使可能的失败场景更清楚,请考虑多重继承,其中使用
static_cast
或dynamic_cast
有时会调整指针值,但reinterpret_cast
会不是。考虑在此示例中从A*
转换为B*
:要了解代码如何以不同的方式失败,请考虑大多数编译器如何实现虚拟函数调用机制:虚拟指针。您的
ChildClassFactory
实例将有一个虚拟指针,指向ChildClassFactory
的虚拟表。现在,当您reinterpret_cast
这个野兽时,它只是偶然“工作”,因为编译器需要一些虚拟指针,指向具有相同/相似布局的虚拟表。但它仍然会包含指向ChildCLassFactory虚拟函数的值,因此这些函数将被调用。所有这些都是在调用未定义行为之后很长一段时间发生的。就好像您开着车跳进一个大峡谷,并想着“嘿,一切都很好”,只是因为您还没有落地。No, it is not. You may not use the result of a
reinterpret_cast
other than to cast stuff back, except for a few special cases:ISO14882:2011(e) 5.2.10-7:
To make a possible failure scenario more clear, consider multiple inheritance, where using a
static_cast
ordynamic_cast
would sometimes adjust the pointer value, butreinterpret_cast
will not. Consider casting in this example fromA*
toB*
:To understand how your code fails in a different way too, consider how most compilers implement the virtual function call mechanism: With virtual pointers. Your instance of
ChildClassFactory
will have a virtual pointer, pointing to the virtual table ofChildClassFactory
. Now when youreinterpret_cast
this beast, it just happens to "work" incidentally, because the compiler expects some virtual pointer, pointing to a virtual table that will have the same/similar layout. But it will still contain the values pointing to theChildCLassFactory
virtual functions, thus these functions would be called. All of this is long after invoking undefined behaviour. It is as if you are jumping with a car into a large canyon and thinking "hey, everything is driving fine" just because you have not hit the ground yet.不,reinterpret_cast 只能用于低级代码,因为它不会执行正确的地址操作。使用 static_cast 或dynamic_cast 代替,
为什么你想要两个工厂,这不适合 GoF 工厂模式。
reinterpret_cast 不是这样做的方法,因为它很慢(运行时检查)并且不是一个很好的 OO 设计(您想要使用语言中的多态性构建)。
相反,在工厂类中创建生成所需类型的构造函数,然后让它们调用各个类型的构造函数。
工厂模式允许您忽略实现中的更改,这是一件好事,因为您可以最大限度地减少依赖关系,并且可以在将来更轻松地维护代码。
No, reinterpret_cast is only to be used for lowlevel code since it will not perfrom the correct address manipulation. Use static_cast or dynamic_cast instead,
Why do you want two factories this does not fit in the GoF factory pattern.
reinterpret_cast is not the way to do it since it is slow (runtime checks) and is not a nice OO design (you want to use the polymophisme build into the language).
Instead make constructors in the factory class that produces the types you are after and then have these call the constructor of the individual types.
The factory pattern allows you to be by ignorant of changes in your implementaion, which is a good thing since you minimize your dependencies, and allows for easier maintainance in the future of the code.
我已经勾选了上面的原始答案(以表彰他),但我想我应该总结一下我在这里学到的东西。
因此,基本问题是没有定义如何实现分派虚拟调用。
这意味着内部用于虚拟呼叫分派的数据结构(例如,vtable)可能在从同一模板创建的模板实例之间位兼容,也可能不兼容。
I've ticked the original answer above (to give him the credit), but I thought I'd sum up what I've learned here.
So, the basic problem is that it's not defined how dispatching virtual calls must be implemented.
This means that the data structure(s) that are internally used for virtual call dispatching (e.g. vtables) may or may not be bit compatible among template instantiations created from the same template.