And*_*eis 8 c++ gcc casting multiple-inheritance void-pointers
我的程序需要使用void*来在动态调用情况下传输数据或对象,以便它可以引用任意类型的数据,甚至是原始类型.但是,我最近发现,在具有多个基类的类的情况下向下转换这些void*的过程失败,甚至在调用这些向下转换指针上的方法后崩溃我的程序,即使内存地址似乎是正确的.在访问"vtable"期间发生崩溃.
所以我创建了一个小测试用例,环境是Mac OS X上的gcc 4.2:
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;
}
Run Code Online (Sandbox Code Playgroud)
产生以下输出:
Decorated (direct)30,30
Shape 30,30
Square 30,30
Decorated (per void*) 73952,73952
DecoratedSquare 30,30
Run Code Online (Sandbox Code Playgroud)
如您所见,"装饰(每个空*)"结果完全错误.它应该像第一行中的30,30一样.
无论我在shape_cast()中使用什么演员方法,我总会得到装饰部分相同的意外结果.这些无效的东西是完全错误的.
根据我对C++的理解,这实际上应该是有效的.有没有机会让这个与void*一起工作?这可能是gcc中的错误吗?
谢谢
Ste*_*sop 12
重复十次 - 你可以安全地使用reinterpret_cast指针做的唯一事情是reinterpret_cast它回到它来自的相同指针类型.转换为void*以下内容同样适用:您必须转换回原始类型.
所以,如果你投了DecoratedSquare*到void*,必须将它转换回DecoratedSquare*.不Decorated*,不Square*,不是Shape*.其中一些可能适用于您的计算机,但这是好运和特定于实现的行为的组合.它通常与单继承一起使用,因为没有明显的理由以一种阻止它工作的方式实现对象指针,但这不能保证,并且它通常不能用于多继承.
你说你的代码通过void*访问"任意类型,包括原始类型".这没有什么不妥 - 想必谁收到数据知道把它当作一个DecoratedSquare*而不是,比如说int*.
如果收到它的人只知道将它视为基类,例如Decorated*,那么无论是谁将它转换为基本类,首先void*应该static_cast是基类,然后void*:
void *decorated_vp = static_cast<Decorated*>(ds);
Run Code Online (Sandbox Code Playgroud)
现在当你decorated_vp回归时Decorated*,你会得到static_cast<Decorated*>(ds)你需要的结果.
这不是编译器错误 - 它是什么reinterpret_cast.该DecoratedSquare对象将在内存中布局如下:
Square
Decorated
DecoratedSquare specific stuff
Run Code Online (Sandbox Code Playgroud)
将指针转换为此指针void*将提供此数据的起始地址,而不知道存在哪种类型.reinterpret_cast<Decorated*>将获取该地址并解释其中的任何Decorated内容 - 但实际的内存内容是Square.这是错误的,因此您会得到未定义的行为.
如果您reinterpret_cast使用正确的动态类型(即DecoratedSquare),则应获得正确的结果,然后转换为基类.
| 归档时间: |
|
| 查看次数: |
3736 次 |
| 最近记录: |