多继承:从void *转换到第二个基类后的意外结果 [英] multiple inheritance: unexpected result after cast from void * to 2nd base class

查看:287
本文介绍了多继承:从void *转换到第二个基类后的意外结果的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的程序需要使用void *为了在动态调用情况下传输数据或对象,以便它可以引用任意类型的数据,甚至是原始类型。但是,我最近发现,在具有多个基类的类中,下载这些void *的过程失败了,甚至在调用这些向下转换的指针上的方法之后,甚至崩溃了我的程序,即使内存地址看起来是正确的。

My program needs to make use of void* in order to transport data or objects in dynamic invocation situation, so that it can reference data of arbitrary types, even primitive types. However, I recently discovered that the process of down-casting these void* in case of classes with multiple base classes fails and even crashes my program after invoking methods on these down casted pointers even if the memory addresses seem to be correct. The crash happens during access to "vtable".

所以我创建了一个小测试用例,环境是Mac OS X上的gcc 4.2:

So I have created a small test case, environment is gcc 4.2 on Mac OS X:

class Shape {
public:
    virtual int w() = 0;
    virtual int h() = 0;
};

class Square : public Shape {
public:
    int l;
    int w() {return l;}
    int h() {return l;}
};

class Decorated {
public:
    int padding;
    int w() {return 2*padding;}
    int h() {return 2*padding;}
};

class DecoratedSquare : public Square, public Decorated {
public:
    int w() {return Square::w() + Decorated::w();}
    int h() {return Square::h() + Decorated::h();}
};


#include <iostream>

template <class T> T shape_cast(void *vp) {
//    return dynamic_cast<T>(vp);   // not possible, no pointer to class type
//    return static_cast<T>(vp);
//    return T(vp);
//    return (T)vp;
    return reinterpret_cast<T>(vp);
}

int main(int argc, char *argv[]) {
    DecoratedSquare *ds = new DecoratedSquare;
    ds->l = 20;
    ds->padding = 5;
    void *dsvp = ds;

    std::cout << "Decorated (direct)" << ds->w() << "," << ds->h() << std::endl;

    std::cout << "Shape " << shape_cast<Shape*>(dsvp)->w() << "," << shape_cast<Shape*>(dsvp)->h() << std::endl;
    std::cout << "Square " << shape_cast<Square*>(dsvp)->w() << "," << shape_cast<Square*>(dsvp)->h() << std::endl;
    std::cout << "Decorated (per void*) " << shape_cast<Decorated*>(dsvp)->w() << "," << shape_cast<Decorated*>(dsvp)->h() << std::endl;
    std::cout << "DecoratedSquare " << shape_cast<DecoratedSquare*>(dsvp)->w() << "," << shape_cast<DecoratedSquare*>(dsvp)->h() << std::endl;
}

产生以下输出:

Decorated (direct)30,30
Shape 30,30
Square 30,30
Decorated (per void*) 73952,73952
DecoratedSquare 30,30

正如你所看到的,Decorated(per void *)结果完全错了。它也应该是30,30喜欢在第一行。

As you can see, the "Decorated (per void*)" result is completely wrong. It should also be 30,30 like in the first line.

无论我在shape_cast()中使用哪种强制转换方法,我总是得到与装饰部分相同的意外结果。这些void *完全错误。

Whatever cast method I use in shape_cast() I will always get the same unexpected results for the Decorated part. Something is completely wrong with these void *.

从我对C ++的理解,这应该是工作。有没有机会得到这个工作与void *?这可能是gcc中的错误吗?

From my understanding of C++ this should be actually working. Is there any chance to get this to work with the void*? Can this be a bug in gcc?

感谢

推荐答案

不是编译器错误 - 它是 reinterpret_cast 的作用。 DecoratedSquare 对象将在内存中排列如下:

It's not a compiler bug - it's what reinterpret_cast does. The DecoratedSquare object will be laid out in memory something like this:

Square
Decorated
DecoratedSquare specific stuff

将指针转换为 void * 将给出这个数据的开始地址,不知道有什么类型。 reinterpret_cast< Decorated *> 将获取该地址并解释为 Decorated - 但实际内存内容是 Square

Converting a pointer to this to void* will give the address of the start of this data, with no knowledge of what type is there. reinterpret_cast<Decorated*> will take that address and interpret whatever is there as a Decorated - but the actual memory contents are the Square. This is wrong, so you get undefined behaviour.

如果你 reinterpret_cast 到正确的结果,你应该得到正确的结果动态类型(即 DecoratedSquare ),然后转换为基类。

You should get the correct results if you reinterpret_cast to the correct dynamic type (that is DecoratedSquare), then convert to the base class.

这篇关于多继承:从void *转换到第二个基类后的意外结果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆