如何在没有确切类型信息的情况下从 void* 转换为虚拟基类?

Yus*_*göz 5 c++ casting multiple-inheritance virtual-inheritance

 class A : public X;
 class B : public virtual A;
 class C : public virtual A;
 class D1 : public B, public C;
 class D2 : public B, public C;

 void *p1 = new D1; //after storing the pointers,
 void *p2 = new D2; //there will be no exact type info.

 A *pA1 = (A *) p1; // Cast 1
 A *pA2 = (A *) p2;
 X *pX1 = (X *) p1; // Cast 2
 X *pX2 = (X *) p2;
Run Code Online (Sandbox Code Playgroud)

这些(演员 1 和演员 2)演员阵容正确吗?(除了未定义的行为。)如果没有确切的类类型信息,我应该如何将它们转换为基类?

编辑1:

请在将问题标记为重复或回答之前先阅读该问题。基类是虚拟的。这是可怕的钻石问题!

请不要关注C-Cast。我知道 static/dynamic/reinterpret_cast 是什么。

编辑2:

void *s 来自 a DWORD_PTR CTreeCtrl::GetItemData(),所以这就是我使用void *. 正如我所拥有的void *那样,我没有关于课程的确切类型的信息。

在可怕的钻石问题之前,只有X, A, B, C-Classes 而没有虚拟继承,因此可以将它们转换为X,因为它是公共基类,在派生类列表中排在第一位(我知道这是未定义的行为,但它作品)。

解决方案1:(最简单且正确的解决方案)

在存储D1D2寻址void *-Pointers 之前,将它们转换为公共基类。之后,向下转换起作用,而且这也不是未定义的行为。

void *p1 = static_cast<X *>(new D1); //or cast to other common base class 'A'
//p2 is the same...

X *pX1 = (X *) p1; //static/reinterpret_cast is better.
//then down cast attempts are possible.
dynamic_cast<D1 *>(pX1); //returns valid pointer
dynamic_cast<D2 *>(pX1); //returns nullptr
Run Code Online (Sandbox Code Playgroud)

解决方案2:(包装类)

存储指向包装类的空指针,该类保存指向基类的指针X,或者A也是可能的。喜欢boost::any

测试1:(虚拟继承之前)

如果没有虚拟继承。我们只有X, A, B, C-类。

B b;
C c;

void *pB = &b;
void *pC = &c;

X *pX1 = (X *) pB;
X *pX2 = (X *) pC;

assert(&b == dynamic_cast<B *>(pX1)); // OK
assert(&c == dynamic_cast<C *>(pX2)); // OK
Run Code Online (Sandbox Code Playgroud)

测试2:(虚拟继承后)D1 d1;D2 d2;

void *pD1 = &d1;
void *pD2 = &d2;

X *pX1 = (X *) pD1;
X *pX2 = (X *) pD2;
A *pA1 = (A *) pD1;
A *pA2 = (A *) pD2;
B *pB1 = (B *) pD1;
B *pB2 = (B *) pD2;
C *pC1 = (C *) pD1;
C *pC2 = (C *) pD2;

assert(&d1 == dynamic_cast<D1 *>(pB1)); //OK
assert(&d2 == dynamic_cast<D2 *>(pB2)); //OK
assert(&d1 == dynamic_cast<D1 *>(pC1)); //Fails
assert(&d2 == dynamic_cast<D2 *>(pC2)); //Fails

assert(&d1 == dynamic_cast<D1 *>(pX1)); //Access violation by casting
assert(&d2 == dynamic_cast<D2 *>(pX2)); //Access violation by casting
assert(&d1 == dynamic_cast<D1 *>(pA1)); //Access violation by casting
assert(&d2 == dynamic_cast<D2 *>(pA2)); //Access violation by casting
Run Code Online (Sandbox Code Playgroud)

我认为通过虚拟继承,第一个虚拟基B类将首先位于内存中。仅使用 VS2015 进行测试

jra*_*nal 1

在 C++ 中,不鼓励使用void*,另一方面,最好使用static_cast<T>()dyanmic_cast<T>()

换句话说,正确的方法是:

A* pA1 = dynamic_cast<A*>(new D1);
Run Code Online (Sandbox Code Playgroud)

在这种情况下,编译器可以检查A和D1之间的关系

如果必须处理void*,最好使用static_cast<T>()重新转换为原始类型,然后再转换为基类。

void* pD1 = new D1;
A* pA1 = dynamic_cast<A*>(static_cast<D1*>(pD1));
Run Code Online (Sandbox Code Playgroud)

编译器除了相信你会给出正确的类之外别无选择......

dynamic_cast一个void*不起作用。

有关 C++ 强制转换的更多信息:检查此问题