将void**铸造为T**的问题
Problems with casting a void** to a T**
TL;DR:我有一个派生的**,我把它作为一个空的用户数据存储在Lua中。然后我试着把它作为一个基地拿回来,结果东西坏了。我能做些什么吗?还是这一切都是注定要失败的疯狂?
详细信息:
我在Lua和C++之间来回传递一些数据,Lua需要使用void*来存储用户数据(我使用Lua并不太重要,只是它使用了void指针(。到目前为止是有道理的。假设我有三个类,Base
和Derived
,其中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和interpret_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
代码不安全。将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没有这些功能。(
这在很大程度上取决于您使用的编译器,但通常指向基类的指针与指向派生类的指针相同。强制施法不应该伤害任何东西。唯一的例外是涉及多个继承;指向一个基类的指针与指向另一基类的指针不同,即使使用相同的对象也是如此。编译器需要知道原始指针的确切类型才能正确调整它,而转换为void*会丢失该信息。
除了Mark Ransom的答案外,如果Derived包含虚拟函数而Base不包含,那么这种类型的转换也会出现问题,但这也是编译器特有的。
- 警告处理为错误这里有什么问题
- 将 C 代码移植到C++,将 void* 从 malloc 转换为所需指针的问题
- 编写完整专业化以识别void类型的问题
- "void value not ignored as it ought to be" - 出了什么问题?
- (c++)无法让 void 函数工作,主.cpp内部的几行也有问题
- 使用 LLVM 从 const void * 转换为模板类型时出现问题
- 从void Pointer(创建通用存储)铸造有什么问题
- const void*对象初始化问题C++
- 在 c++ 中使用 sqlite 的 void 函数中的"返回"问题
- 修复退货问题.警告:控件到达C++中非void函数的末尾
- void * 转换为 int 语法的问题
- 在将 void 指针转换为结构时获取值时遇到问题
- void main() 有什么问题
- 使用mpl::if_、boost::函数和void类型定义的问题
- 在这段代码中,我有一个警告:控制达到非void函数的结束[- return-type].我怎样才能解决这个问题
- 布尔和Void问题
- void*指针中的引用问题
- 将void**铸造为T**的问题
- void() 问题 - 它不打印结果
- GCC 问题 - 从 'void (*)(MyObject*, bool)' 到 'const void*' 的转换无效