将空隙**投射到T **的问题

时间:2011-08-04 04:44:24

标签: c++ casting

TL; DR:我有一个Derived **,我在Lua中存储为void * userdata。然后我尝试将它作为基础**和东西打破。有什么我可以做的,或者这是注定要失败的所有疯狂吗?

详细说明:

我在Lua和C ++之间来回传递一些数据,Lua需要使用void *来存储userdata(我使用Lua并不太重要,除了它使用void指针)。到目前为止有道理。假设我有三个类BaseDerived,其中Derived继承自Base。我向Lua提供的用户数据是一个指向指针的指针,如下所示:

template <typename T>
void lua_push(L, T* obj) {
    T** ud = (T**)lua_newuserdata(L, sizeof(T*)); // Create a new userdata
    *ud = obj; // Set a pointer to my object
    // rest of the function setting up other stuff omitted
}

当然,这是一个很好的模板化函数,所以我可以用这种方式传递我的三种类型中的任何一种。稍后我可以使用另一个模板化函数将我的用户数据从Lua中取出,如下所示:

template <typename T>
T* lua_to(lua_State* L, int index) { 
    // there's normally a special metatable check here that ensures that 
    // this is the type I want, I've omitted it for this example
    return *(T**)lua_touserdata(L, index);
}

当我传入和传出相同的类型时,这可以正常工作。我尝试将Derived作为Base拉出来时遇到了问题。

在我的具体情况下,我有一个存储在Base上的矢量。我使用lua_push<Derived>(L, obj);将我的对象推送到Lua。之后,在另一个地方,我使用Base* obj = lua_to<Base>(L, i);将其拉出来。然后我push_back将一些东西放入我的载体中。稍后,代码的另一部分拉出完全相同的对象(使用指针比较验证),除非这次使用Derived* obj = lua_to<Derived>(L, i);我的Derived对象没有看到被推入的对象。我相信我我把这缩小到不正确的演员阵容,当我打电话给push_back

时,我可能会在某处弄坏一些记忆

所以我的问题是,有没有办法让这个演员工作正确?我尝试过各种各样的演员阵容。 static_cast,dynamic_cast和reinterpret_cast似乎不起作用,要么给我相同的错误答案,要么根本不编译。

具体示例:

Base* b = lua_to<Base>(L, -1); // Both lua_to's looking at the same object
Derived* d = lua_to<Derived>(L, -1); // You can be double sure because the pointers in the output match
std::cout << "Base: " << b << " " << b->myVec.size() << std::endl;
std::cout << "Derived: " << d << " " << d->myVec.size() << std::endl;

输出:

Base: 0xa1fb470 1
Derived: 0xa1fb470 0

3 个答案:

答案 0 :(得分:3)

代码不安全。当您将Base *投射到void *时,您应始终先将void *投回Base *然后然后再次投放到Derived * 。如此:

Derived *obj = ...;
Base** ud = reinterpret_cast<Base **>(lua_newuserdata(L, sizeof(Base*)));
*ud = obj; // implicit cast Derived -> Base
...
Derived *obj = static_cast<Derived *>(*ud); // explicit Base -> Derived

基本上来说,

Y -> X -> void* -> X -> Y (safe)
Y -> X -> void* -> Y (unsafe)

这是因为如果两个指针具有不同的类型,则指向同一对象的两个指针的实际指针值可能不同。它是否有效取决于继承和虚函数等各种因素。 (它总是在C中工作,因为C没有这些功能。)

答案 1 :(得分:2)

这完全取决于您正在使用的编译器,但通常指向基类的指针与指向派生类的指针相同。做强制演员不应伤害任何事情。唯一的例外是涉及多个继承时;指向一个基类的指针与指向另一个基类的指针不同,即使使用相同的对象也是如此。编译器需要知道原始指针的确切类型才能正确调整它,并且转换为void *会丢失该信息。

答案 2 :(得分:1)

除了Mark Ransom的回答之外,如果Derived包含虚函数而Base不包含此类投射,您也会遇到问题,但这又是编译器特定的。