[英]Is it unsafe to store derived object to void*, then cast base object out of it?
For example: 例如:
class Base1 {};
class Base2 {};
class Derived: publid Base1, public Base2 {};
// object is stored on a void* slot
void* void_slot = new Derived();
// ... many decades after ...
//object is fetched from the void* slot
Base2* obj = (Base2*) void_slot;
obj->some_base2_method();
I think it is probably unsafe. 我认为这可能不安全。 Does
dynamic_cast<>
solve this issue? dynamic_cast<>
是否解决了这个问题?
Base2* obj = dynamic_cast<Base2*> void_slot;
More backgrounds : 更多背景 :
I'm working on calling C++ library from Perl. 我正在从Perl调用C ++库。 When you construct an C++ object, it is stored in the integer slot of a Perl value (the
IV
value of a SV
), which is like a void*
; 构造C ++对象时,它存储在Perl值的整数槽中(
SV
的IV
值),就像void*
; and when you call methods, the object pointer is cast from IV
, and the corresponding C++ method is called using the object pointer. 当你调用方法时,对象指针从
IV
,并使用对象指针调用相应的C ++方法。 Thus I guess it could be problematic, as the pointer to base type can be different with the pointer to derived type, especially when there are multiple inheritance. 因此我猜它可能有问题,因为指向基类型的指针可能与指向派生类型的指针不同,尤其是当存在多个继承时。
I've posted a similar question on PerlMonks, but did not get much response from there. 我在PerlMonks上发布了一个类似的问题,但没有得到太多响应。 So I ask it here, from the aspect of C++.
所以我在这里问一下,从C ++的角度来看。
Yes it is unsafe, but will probably cause no errors in your example due to the empty base optimization . 是的,它不安全,但由于空基优化 ,可能不会在您的示例中造成错误。 Consider instead the following example:
请考虑以下示例:
class Base1 { int b1; };
class Base2 { int b2; };
class Derived : public Base1, public Base2 { int d; };
The memory layout for an object of type Derived
will probably look like this: Derived
类型的对象的内存布局可能如下所示:
0123456789AB
[b1][b2][ d]
^ begin of Derived
^ begin of Base1
^ begin of Base2
Now, a pointer to Derived
and to Base1
will have the same numerical value, but one to Base2
will be different. 现在,指向
Derived
和Base1
的指针将具有相同的数值,但对Base2
的指针将有所不同。 To change the numerical value appropriately, the compiler has to know that you are converting a Derived*
to a Base2*
. 要适当地更改数值,编译器必须知道您正在将
Derived*
转换为Base2*
。 This is not possible when casting it to void*
in between, since the value of the void*
could just as well have come from a Base2*
. 铸造它的时候,这是不可能
void*
之间,因为值void*
也可以同样都来自一个Base2*
。
In fact, a conversion sequence like static_cast<T*>(static_cast<void*>(x))
is exactly how reinterpret_cast<T*>(x)
is defined. 实际上,像
static_cast<T*>(static_cast<void*>(x))
这样的转换序列正是如何定义reinterpret_cast<T*>(x)
的。 And you would not assume that reinterpret_cast
is safe to randomly use an arbitrary types - would you? 并且你不会认为
reinterpret_cast
随机使用任意类型是安全的 - 你会吗?
While one might believe that dynamic_cast
might help here, it is in fact not even applicable! 虽然有人可能认为
dynamic_cast
可能对此有所帮助,但实际上它甚至都不适用! Since dynamic_cast
is supposed to use run time type information to guarantee that a cast is possible, its target needs to be a pointer (or reference) to a class type with at least one virtual member. 由于
dynamic_cast
应该使用运行时类型信息来保证可以进行强制转换,因此其目标需要是具有至少一个虚拟成员的类类型的指针(或引用)。 In this case, the target is not even a pointer to a complete type, but to void
. 在这种情况下,目标甚至不是指向完整类型的指针,而是
void
。
No matter what you do afterwards you must retrieve the same type of pointer that you stored (with a sole exception for interpreting your object as a char
array). 无论你以后做什么,你必须检索你存储的相同类型的指针(唯一的例外是将对象解释为
char
数组)。 The obvious solution would be, to either always store a pointer to a common base class like 显而易见的解决方案是,始终存储指向公共基类的指针
void* void_slot = static_cast<CommonBase*>(input);
CommonBase* output = static_cast<CommonBase*>(void_slot);
or to use an intermediate class that knows which kind of pointer you are talking about 或者使用一个知道你正在谈论哪种指针的中间类
struct Slotty {
enum class type_t {
Base1,
Base2,
Derived
} type;
void* ptr;
Slotty(Base1* ptr) : type(type_t::Base1), ptr(ptr) { }
Slotty(Base2* ptr) : type(type_t::Base2), ptr(ptr) { }
Slotty(Derived* ptr) : type(type_t::Derived), ptr(ptr) { }
};
void* void_slot = static_cast<void*>(new Slotty(input));
Slotty* temp = static_cast<Slotty*>(void_slot);
switch(Slotty.type) {
case Slotty::type_t::Base1:
/* do sth with */ static_cast<Base1*>(temp.ptr);
break;
case Slotty::type_t::Base2:
/* do sth with */ static_cast<Base2*>(temp.ptr);
break;
case Slotty::type_t::Derived:
/* do sth with */ static_cast<Derived*>(temp.ptr);
break;
}
If you have complete control over your classes, just create a single virtual root base. 如果您可以完全控制类,只需创建一个虚拟根目录。 Cast to that first before you cast to
void *
, then cast back to that first. 在转换为
void *
之前先转换为第一个,然后再转换回第一个。 Then you can use dynamic_cast
to cast to whatever derived type you want: 然后,您可以使用
dynamic_cast
转换为您想要的任何派生类型:
struct Root {
virtual ~Root() {}
};
struct Base1 : virtual public Root { };
struct Base2 : virtual public Root { };
struct Derived1 : public Base1, public Base2 { };
struct Derived2 : public Derived1 { };
int main() {
Derived1 *d1 = new Derived1;
Derived2 *d2 = new Derived2;
void *vp = static_cast<Root *>(d1);
Derived1 *d11 = dynamic_cast<Derived1 *>(static_cast<Root *>(vp));
vp = static_cast<Root *>(d2);
Derived2 *d22 = dynamic_cast<Derived2 *>(static_cast<Root *>(vp));
delete d1;
delete d2;
}
EDIT: Apparently the classes must be polymorphic and you must use dynamic_cast, so put a trivial virtual destructor in Root. 编辑:显然这些类必须是多态的,你必须使用dynamic_cast,所以在Root中放入一个简单的虚拟析构函数。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.