虚函数和强制转换为void和返回
Virtual functions and cast to void and back
目前我正在使用一个遗留的c++代码库。在此代码库中,指向对象的指针被转换为空指针,然后存储在c库中。考虑下面的代码:
class interface {
public:
virtual void foo() {
std::cout << "Interface" << std::endl;}
virtual ~interface(){};
};
class debug_interface: public interface {
public:
virtual void foo() {
std::cout << "Debug Interface" << std::endl;}
};
对象interface
和debug_interface
被分配到堆上,地址被存储到一个void指针中。在某些时候,指针被检索,然后被强制转换回基类interface
。然后调用虚函数调用。看到
int main(int argc, char *argv[]){
void *handle = reinterpret_cast<void*>(new interface());
void *debug_handle = reinterpret_cast<void*>(new debug_interface());
//void *handle = new interface();
//void *debug_handle = new debug_interface();
interface *foo1 = reinterpret_cast<interface*>(handle);
interface *foo2 = reinterpret_cast<interface*>(debug_handle);
//interface *foo1 = static_cast<interface*>(handle);
//interface *foo2 = static_cast<interface*>(debug_handle);
foo1->foo();
foo2->foo();
return 0;
}
首先我不明白,为什么要用reinterpret_cast。据我所知,指向对象的指针可以隐式地转换为void*。此外,要使这种强制转换显式,一个static_cast就足够了,对吗?但更重要的问题是:将指针debug_handle强制转换为interface*(而不是debug_interface*)并调用虚拟调用真的节省吗?根据c++标准,(5.2.10)这是未定义的行为:
指向对象的指针可以显式转换为指针到不同的对象类型当一个"指针"类型的右值v将"指向T1"类型转换为"指向cv T2"类型,结果为如果T1和T2同时存在,则static_cast(static_cast(v))标准布局类型(3.9)和对齐要求是T2并不比T1严格。类型的右值转换将"指向T1的指针"转换为"指向T2的指针"类型(其中T1和T2分别为)对象类型,其中T2的对齐要求没有比T1更严格),并恢复到原来的类型收益率原始指针值。任何其他此类指针的结果未指定转换。
从handle
到foo1
的转换应该是可以的,但我可以再次使用static_cast?
编辑我的示例源代码是错误的。Debug_interface是interface的派生类。
免责声明: 第一部分是在两个接口没有通过继承关联的情况下编写的。
未定义的行为实际上发生在这里:
foo2->foo();
在这里,您正在使用interface
API在指向对象的指针上,该对象是而不是实现该API。事实上,interface
和debug_interface
碰巧都将foo()
成员实现为它们的第一个方法,这不会改变任何东西:这些类不是通过继承相关的,因此它们是不兼容的。
您引用的摘录处理了转换本身被允许的情况。在您的情况下,我的理解是,您实际上可以将指向debug_interface
的指针转换为指向interface
的指针:然而,您现在可以对指向接口的指针做的唯一安全的事情是将其转换回debug_interface
指针:使用它访问interface
成员是不安全的。
EDIT:如果debug_interface
公开派生自interface
,这是一个不同的问题。
debug_interface*
强制转换到interface*
是完全安全的:从派生到基的转换甚至可以由编译器隐式地应用。但是为了安全起见,必须直接进行强制转换,可以通过:
- a
static_cast
- a
dynamic_cast
(这将引入运行时检查,对于向上转换来说是过度的)。
通过两个reinterpret_cast
执行此操作是一种未定义的行为:它可能适用于单继承(在某些编译器上),但绝对不受标准的保证。
通过两个static_cast
执行此操作也将是未定义行为。标准保证(强调我的):
指向对象的指针类型的值转换为"指向cv void的指针"并返回到原始指针类型将保持其原始值。
在你的例子中,你并没有转换回原来的指针,而是转换回另一种指针类型:标准并没有给你任何保证你将得到的值。
可能的解决方案知道:
- 直接从
debug_interface
转换为interface
是安全的 - 从指向对象类型的指针转换为
void *
然后再转换回指向相同对象类型的指针是安全的。
你可以把它们组合起来得到一个标准的保证解决方案:
// Safe, see point #1.
// The new expression returns a debug_interface* and static_cast applies a derived-to-base conversion.
interface *debug_handle_interim = static_cast<interface*>(new debug_interface());
// Convert a interface* to void* then back to interface*, see #2
void *type_erased = static_cast<void*>(debug_handle_interim);
interface *debug_handle_back = static_cast<interface*>(type_erased);
您是对的,它是未定义的。当转换回void*
类型时,应该始终使用原始指针类型。您应该使用static_cast
而不是dynamic_cast
。
所以你的代码可以安全地写成:
int main(int argc, char *argv[]){
void *handle = new interface();
void *debug_handle = static_cast<interface*>(new debug_interface());
//Beware! This would be wrong:
//void *debug_handle = new debug_interface();
interface *foo1 = static_cast<interface*>(handle);
interface *foo2 = static_cast<interface*>(debug_handle);
foo1->foo();
foo2->foo();
return 0;
}
如果你碰巧有一个类,比如:
class weird_interface : something, public interface
{ /* */};
然后写:
weird_interface *a = new weird_interface();
interface *b = static_cast<interface*>(static_cast<void*>(a));
interface *c = a;
static_assert(b == c, "");
你会明白为什么UB。
这是标准不允许的。存储指针的正确方法是:
interface* tmp = new debug_interface;
void* handle = reinterpret_cast<void*>(tmp);
你当然可以在一行完成:
void* handle = reinterpret_cast<void*>(
static_cast<interface*>(new debug_interface));
但是这对我来说太笨拙而且容易出错。
标准允许您将指针强制转换为void*
并返回,但您必须将其强制转换回与开始时完全相同的指针类型。指向基类的指针不可替代。
如果您让debug_interface
使用多重继承或虚拟继承,那么您的代码很有可能崩溃和燃烧,但即使使用普通的单一继承,它也不符合。
您是对的,这段代码通常在类的第二个接口时中断。
- Boost buffer_cast 无法从 void* 转换为 PointerToPodType
- 将void*转换为某种类型的shared_ptr
- 如何将 void* 转换为函数指针?
- 对 C++ 中的特定标头使用 extern C 关键字是否允许从 void* 转换为 char*?
- 如何将 void 转换为 typedef void(..)(浮动)
- 无法将参数 1 从"int (__thiscall A::* *)(void)"转换为"int (__cdecl *)(void)"
- 从 'void *' 转换为 'const std::string *&' 必须具有所有中间指针 const 合格
- x = malloc(n * sizeof (int));无法将 void 转换为 int
- 错误:请求从“ void”转换为“ QString”的非量表类型
- 使用 LLVM 从 const void * 转换为模板类型时出现问题
- 如何修复错误:请求从"void"转换为非标量类型"std::vector<int>"
- void* 转换为基类并调用虚函数
- 将 void* 转换为 std::function<void()>
- 无法将参数 4 从 'void' 转换为'const char *'
- C++11 无法将 std::condition_variable::wait 从 'void' 转换为 'bool'
- 如何在 C++11 中将"const void*"转换为函数指针?
- 似乎无法在 c++ 中将 void* 转换为结构
- 将 void 转换为字节
- void * 转换为 int 语法的问题
- C++从类型“void*”转换为类型“double”无效