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

时间:2017-03-14 16:09:29

标签: 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;

那些(演员1和演员阵容2)演员是否正确? (未定义的行为除外。) 如何在没有确切类类信息的情况下将它们转换为基类?

修改1:

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

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

编辑2:

void *来自DWORD_PTR CTreeCtrl::GetItemData(),所以这就是我使用void *的原因。由于我有void *,我没有关于类的确切类型的信息。

在可怕的钻石问题之前,只有X, A, B, C - 类,没有虚拟干扰,因此有可能将它们转换为X,因为它是公共基类,它首先出现在派生类列表(我知道这是未定义的行为,但它有效)。

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

D1 - 指针中存储D2void *地址之前,将它们转换为公共基类。在那之后,向下投射工作,这也不是一个未定义的行为。

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

解决方案2:(包装类)

存储一个void指针,指向包含指向基类XA的指针的包装类也是可能的。与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

测试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

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

1 个答案:

答案 0 :(得分:2)

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

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

A* pA1 = dynamic_cast<A*>(new D1);

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

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

void* pD1 = new D1;
A* pA1 = dynamic_cast<A*>(static_cast<D1*>(pD1));

编译器别无选择,只相信你会给出正确的类......

dynamic_cast void*不起作用。

有关c ++强制转换的更多信息:check this question