例如:
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();
Run Code Online (Sandbox Code Playgroud)
我认为这可能不安全.不dynamic_cast<>解决这个问题?
Base2* obj = dynamic_cast<Base2*> void_slot;
Run Code Online (Sandbox Code Playgroud)
更多背景:
我正在从Perl调用C++库.构造C++对象时,它存储在Perl值的整数槽中(a的IV值SV),就像a void*; 当您调用方法时,将从中转换对象指针IV,并使用对象指针调用相应的C++方法.因此我猜它可能有问题,因为指向基类型的指针可能与指向派生类型的指针不同,尤其是当存在多个继承时.
我在PerlMonks上发布了一个类似的问题,但没有得到太多响应.所以我在这里问一下,从C++的角度来看.
是的,它不安全,但由于空基优化,可能不会在您的示例中造成错误.请考虑以下示例:
class Base1 { int b1; };
class Base2 { int b2; };
class Derived : public Base1, public Base2 { int d; };
Run Code Online (Sandbox Code Playgroud)
类型对象的内存布局Derived可能如下所示:
0123456789AB
[b1][b2][ d]
^ begin of Derived
^ begin of Base1
^ begin of Base2
Run Code Online (Sandbox Code Playgroud)
现在,指向Derived和的指针Base1将具有相同的数值,但其中一个Base2将是不同的.要适当地更改数值,编译器必须知道您Derived*要将a 转换为a Base2*.将它投射到void*中间时是不可能的,因为它的价值void*也可以来自a Base2*.
实际上,转换序列static_cast<T*>(static_cast<void*>(x))就是如何reinterpret_cast<T*>(x)定义的.并且你不会认为reinterpret_cast随机使用任意类型是安全的 - 你会吗?
虽然人们可能认为这dynamic_cast可能有所帮助,但实际上甚至不适用!由于dynamic_cast应该使用运行时类型信息来保证可以进行强制转换,因此其目标需要是具有至少一个虚拟成员的类类型的指针(或引用).在这种情况下,目标甚至不是指向完整类型的指针,而是指向void.
无论你以后做什么,你必须检索你存储的相同类型的指针(唯一的例外是将对象解释为char数组).显而易见的解决方案是,始终存储指向公共基类的指针
void* void_slot = static_cast<CommonBase*>(input);
CommonBase* output = static_cast<CommonBase*>(void_slot);
Run Code Online (Sandbox Code Playgroud)
或者使用一个知道你正在谈论哪种指针的中间类
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;
}
Run Code Online (Sandbox Code Playgroud)