[英]Does multiple inheritance order matters?
我想出了一些令人困惑的情况。 下面是他们。
#include <iostream>
using namespace std;
class Base {
public:
Base(int num) : data(num) {
cout << "BASE : " << num << endl;
}
virtual ~Base() = default;
virtual void func1() {
cout << "Base func1 called : " << data << endl;
}
virtual void func3() {
cout << "Base func3 called : " << data << endl;
}
private:
int data;
};
class Interface {
public:
Interface() {
cout << "INTERFACE : " << endl;
}
virtual ~Interface() = default;
virtual void func2() {
cout << "Interface func2 called" << endl;
}
};
class Derived : public Interface, public Base {
public:
Derived() : Base(0) {
cout << "DERIVED : hh" << endl;
}
virtual ~Derived() = default;
virtual void func1() override {
cout << "Derived fuc1 called" << endl;
}
virtual void func3() override {
cout << "Derived fuc3 called" << endl;
}
virtual void func2() override {
cout << "Derived fuc2 called" << endl;
}
};
int main() {
//Interface* a = new Derived(); // derived func2 called
//Base* a = new Derived(); // derived func1 called
//Derived* a = new Derived(); // derived func2 called
void* a = new Derived(); // derived func1 called
auto b = (Interface*)a;
b->func2();
...
}
执行b->func2()
时,结果因变量 a 的显式类型而异。
结果在评论中。
为什么在执行b->func2()
时它们会有所不同?
void* a = new Derived(); // derived func1 called
auto b = (Interface*)a;
这是未定义的行为。 The usual semantics of converting a pointer to a derived class to a pointer to its base class apply only, well, when you convert a pointer to a derived class into a pointer to its base class. 但这不是这里发生的事情。
到其他指针的中间转换,如void *
会使所有保修失效(双关语不是有意的)。
void* a = new Derived();
auto b = (Interface*)a;
这是未定义的行为。 您没有将void*
转换回存储在其中的相同类型( Derived*
)。
如果要将void*
转换为Interface*
,则需要将Interface*
存储在void*
中,例如:
void* a = static_cast<Interface*>(new Derived());
auto b = static_cast<Interface*>(a);
When a virtual
method is called via an object pointer, the compiler dereferences the object pointer to access a hidden pointer to a vtable belonging to the type that the object pointer is pointing at, and then it indexes into that vtable to know which class method to称呼。
Derived
的 object 在 memory 中看起来像这样(细节可能因编译器而异,但这是它的要点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
object 由Base
、 Interface
和Derived
拼凑在一起的所有内容组成。 每个 class 仍然有自己的指向属于该 class 的虚拟方法表的指针。 但是由于Derived
实现了所有的virtual
方法,一个Derived
object 有多个 vtable 指针,它们都引用了Derived
的方法。
1:为了提高效率, Derived
很可能只有 1 个 vtable,并且它的所有 3 个 vmt 指针都指向该单个表的不同区域。 但这是一个实现细节,对于这个答案来说并不重要。
当a
被声明为Interface*
时,它指向Derived
object 的Interface
部分:
+---------------+
| Base vmt |
|---------------|
a -> | Interface vmt |
|---------------|
| Derived vmt |
+---------------+
将a
类型转换为Interface*
实际上是一个无操作,导致b
是一个Interface*
指针,指向Derived
object 的Interface
部分:
+---------------+
| Base vmt |
|---------------|
b -> | Interface vmt |
|---------------|
| Derived vmt |
+---------------+
因此调用b->func2()
使用Interface'
的 vtable,如预期的那样跳转到Derived::func2()
的第二个条目。
当a
被声明为Base*
时,它指向Derived
object 的Base
部分:
+---------------+
a -> | Base vmt |
|---------------|
| Interface vmt |
|---------------|
| Derived vmt |
+---------------+
但是将a
类型转换为Interface*
是Undefined Behavior ,因为Base
和Interface
彼此不相关,导致b
是Interface*
指向Derived
object 的Base
部分的指针:
+---------------+
b -> | Base vmt |
|---------------|
| Interface vmt |
|---------------|
| Derived vmt |
+---------------+
因此调用b->func2()
错误地使用Base
的 vtable 而不是Interface
的 vtable,意外地跳转到第二个条目Derived::func1()
。
如果你在这里使用static_cast
而不是 C 风格的转换,编译就会失败(这就是为什么你应该避免在 C++ 中使用 C 风格的转换。)。
当a
被声明为Derived*
时,它指向Derived
object 的Derived
部分:
+---------------+
| Base vmt |
|---------------|
| Interface vmt |
|---------------|
a -> | Derived vmt |
+---------------+
将a
类型转换为Interface*
是明确定义的,因为Interface
是Derived
的基础,导致b
是指向Derived
object 的Interface
部分的Interface*
指针:
+---------------+
| Base vmt |
|---------------|
b -> | Interface vmt |
|---------------|
| Derived vmt |
+---------------+
因此调用b->func2()
使用Interface
的 vtable,如预期的那样跳转到Derived::func2()
的第二个条目。
当a
被声明为void*
时,它指向Derived
object 的Derived
部分:
+---------------+
| Base vmt |
|---------------|
| Interface vmt |
|---------------|
a -> | Derived vmt |
+---------------+
但是将a
类型转换为Interface*
是Undefined Behavior ,因为a
没有指向Derived
object 的Interface
部分,从而导致Interface*
指针指向Derived
object 的Derived
部分:
+---------------+
| Base vmt |
|---------------|
| Interface vmt |
|---------------|
b -> | Derived vmt |
+---------------+
因此调用b->func2()
错误地使用Derived
的 vtable 而不是Interface
的 vtable,意外地跳转到第二个条目Derived::func1()
。
行为未定义,因为您没有将void *
转换回实际类型。
你要么需要做
void *a = (Interface*)(new Derived());
auto b = (Interface *)a;
或者在你们两个之间增加一个额外的步骤
void* a = new Derived();
auto temp = (Derived *)a; // explicitly convert the void pointer to the actual type
auto b = (Interface*)temp;
这相当于
void* a = new Derived();
auto b = (Interface*)((Derived *)a);
无论哪种方式,如果在转换为Interface *
之前没有将void *
转换为Derived *
,则行为是未定义的。
考虑使用_cast
之一(例如static_cast
)进行转换,而不是使用 C 风格的强制转换。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.