reinterpret_cast和交叉铸造

时间:2013-04-16 11:48:29

标签: c++ reinterpret-cast

#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是纯虚拟的,它是否正常?

5 个答案:

答案 0 :(得分:3)

reinterpret_cast不安全;你应该总是使用dynamic_castI1I2是纯虚拟类是无关紧要的;作为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