void* a = new Derived();
auto b = (Interface*)a;
这是未定义的行为。您没有将void*
back 转换为存储在其中的相同类型 ( Derived*
)。
如果您想将 to 转换为void*
,Interface*
则需要将 an 存储Interface*
在void*
开头,例如:
void* a = static_cast<Interface*>(new Derived());
auto b = static_cast<Interface*>(a);
当virtual
通过对象指针调用方法时,编译器取消引用对象指针以访问指向属于对象指针指向的类型的 vtable 的隐藏指针,然后它索引到该 vtable 以知道要调用哪个类方法.
该Derived
对象在内存中看起来像这样(细节可能因编译器而异,但这是它的要点1):
+-------+
+-> | ~Base |--> &Derived::~Derived()
| | func1 |--> &Derived::func1()
+---------------+ | | func3 |--> &Derived::func3()
| Base vmt |--+ +-------+
|---------------| +------------+
| Interface vmt |------> | ~Interface |--> &Derived::~Derived()
|---------------| | func2 |--> &Derived::func2()
| Derived vmt |--+ +------------+
+---------------+ | +------------+
+-> | ~Base |--> &Derived::~Derived()
| func1 |--> &Derived::func1()
| func3 |--> &Derived::func3()
| ~Interface |--> &Derived::~Derived()
| func2 |--> &Derived::func2()
| ~Derived |--> &Derived::~Derived()
+------------+
一个Derived
对象由 中的所有内容组成Base
,Interface
并Derived
拼凑在一起。每个类仍然有自己的指向属于该类的虚拟方法表的指针。但是由于Derived
实现了所有的virtual
方法,一个Derived
对象有多个 vtable 指针,它们都引用Derived
的方法。
1:为了效率,很可能Derived
只有 1 个 vtable,并且它的所有 3 个 vmt 指针都指向该单个表的不同区域。但这是一个实现细节,对于这个答案来说并不重要。
当a
被声明为Interface*
时,它指向对象的Interface
一部分Derived
:
+---------------+
| Base vmt |
|---------------|
a -> | Interface vmt |
|---------------|
| Derived vmt |
+---------------+
类型转换a
到Interface*
实际上是一个无操作,导致b
成为Interface*
指向对象Interface
部分的指针Derived
:
+---------------+
| Base vmt |
|---------------|
b -> | Interface vmt |
|---------------|
| Derived vmt |
+---------------+
因此调用b->func2()
使用Interface'
s vtable,Derived::func2()
如预期的那样跳转到第二个条目。
当a
被声明为Base*
时,它指向对象的Base
一部分Derived
:
+---------------+
a -> | Base vmt |
|---------------|
| Interface vmt |
|---------------|
| Derived vmt |
+---------------+
但是类型转换a
为Undefined Behavior,因为and彼此不相关,导致Interface*
成为指向对象部分的指针:Base
Interface
b
Interface*
Base
Derived
+---------------+
b -> | Base vmt |
|---------------|
| Interface vmt |
|---------------|
| Derived vmt |
+---------------+
所以调用b->func2()
错误地使用Base
了 's vtable 而不是's vtable ,意外地Interface
跳转到了第二个条目。Derived::func1()
如果您static_cast
在这里使用而不是 C 风格的转换,编译就会失败(这就是为什么您应该避免在 C++ 中使用 C 风格的转换!)。
当a
被声明为Derived*
时,它指向对象的Derived
一部分Derived
:
+---------------+
| Base vmt |
|---------------|
| Interface vmt |
|---------------|
a -> | Derived vmt |
+---------------+
类型转换a
toInterface*
是明确定义的,因为Interface
它是 的基础Derived
,因此b
是Interface*
指向对象Interface
部分的指针Derived
:
+---------------+
| Base vmt |
|---------------|
b -> | Interface vmt |
|---------------|
| Derived vmt |
+---------------+
因此调用b->func2()
使用的 vtable ,如预期的那样Interface
跳转到第二个条目。Derived::func2()
当a
被声明为void*
时,它指向对象的Derived
一部分Derived
:
+---------------+
| Base vmt |
|---------------|
| Interface vmt |
|---------------|
a -> | Derived vmt |
+---------------+
但是类型转换a
为Undefined Behavior,因为Interface*
没有指向对象的部分,导致指向对象部分的指针:a
Interface
Derived
Interface*
Derived
Derived
+---------------+
| Base vmt |
|---------------|
| Interface vmt |
|---------------|
b -> | Derived vmt |
+---------------+
所以调用b->func2()
错误地使用Derived
了 's vtable 而不是's vtable ,意外地Interface
跳转到了第二个条目。Derived::func1()