#include<iostream>
struct I1
{
virtual void nb1()=0;
virtual ~I1(){}
};
struct I2
{
virtual void nb2()=0;
virtual void nb22()=0;
virtual ~I2(){}
};
struct C12 : I1, I2
{
virtual void nb1(){std::cout << "nb\n";}
virtual void nb2(){std::cout << "nb2\n";}
virtual void nb22(){std::cout << "nb22\n";}
};
int main()
{
I2 * p2 = new C12;
I1 * p1 = reinterpret_cast<I1*>(p2);
return 1;
}
在这里使用reinterpret_cast
是否有禁忌症?我应该强制使用dynamic_cast
吗?
如果I1和I2是纯虚拟的,它是否正常?
答案 0 :(得分:3)
reinterpret_cast
不安全;你应该总是使用dynamic_cast
。 I1
和I2
是纯虚拟类是无关紧要的;作为C12
的子对象,它们具有非零存储要求。的确,在以下计划中:
int main() {
I2 * p2 = new C12;
I1 * p1 = reinterpret_cast<I1*>(p2);
std::cout << p2 << '\n';
std::cout << p1 << '\n';
std::cout << dynamic_cast<I1*>(p2) << '\n';
p1->nb1();
}
输出是:
0x14de0c8
0x14de0c8
0x14de0c0 // !!!
nb2 // !!!
你可能会想到empty base optimization(1.8p5);但这不适用于具有虚拟方法的类,这需要vptr。
答案 1 :(得分:3)
在这里使用
reinterpret_cast
是否有禁忌症?
不要那样做;它会给出不确定的行为。
通常,基础子对象存储在完整对象内的不同位置,因此转换将最终指向错误的位置。您可能会发现它与这些空基类偶然“起作用”,因为它们可能(或可能不会)最终位于同一位置;但你当然不能依赖这种行为。
我应该强制使用
dynamic_cast
吗?
如果在编译时不知道公共派生类型(C12
),那么这是唯一明智的选择。请注意,它需要多态类(这是这种情况)。
如果您确实知道常见的派生类型,那么您可以使用static_cast
:
I1 * p1 = static_cast<C12*>(p2);
请注意,派生类的“upcast”可以隐式完成;只有来自基类的“向下倾斜”需要一个明确的演员。
答案 2 :(得分:1)
由于编译器可能为C12的两个子对象中的每一个保留一些字节,即使它们没有成员或基类,并且从那时起这两个子对象的地址是不同的,这可能导致问题。该标准只是说该演员表的结果未指定(§5.2.10,7)。
答案 3 :(得分:0)
如果你有一个类型为C12的对象x,并且有三个指向它的指针,如:
C12 x;
C12 *pA = &x;
I1 *pB = &x;
I2 *pC = &x;
那么指针pA,pB和pC不一定相等。所以你不能只在pA,pB和pC之间来回重新解释。您可以安全地在pA和pB之间以及pA和pC之间进行static_cast或dynamic_cast。因此,要从I2 *获得I1 *,您需要首先将p2转换为C12 *,然后转换为I1 *。选择你最喜欢的行:
I1 * p1 = static_cast<C12*>(p2);
I1 * p1 = dynamic_cast<C12*>(p2);
答案 4 :(得分:0)
使用reinterpret_cast
几乎总是坏主意。
您可以在这里dynamic_cast
或双static_cast
(通过基类)。
从ABI的角度来看,C12
是:
struct C12 {
I1 parent1;
I2 parent2;
};
C12 obj;
现在您已获得指向内部obj.parent2
的指针,而不是obj
。