[英]Does multiple inheritance order matters?
I have come up with some confusing situations.我想出了一些令人困惑的情况。 Below are they.下面是他们。
#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();
...
}
When executing b->func2()
, the result is different by the explicit type of variable a.执行b->func2()
时,结果因变量 a 的显式类型而异。
The results are in the comments.结果在评论中。
Why are they different when executing b->func2()
?为什么在执行b->func2()
时它们会有所不同?
void* a = new Derived(); // derived func1 called
auto b = (Interface*)a;
This is undefined behavior.这是未定义的行为。 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. 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. But that's not what happens here.但这不是这里发生的事情。
An intermediate conversion into some other pointer, like a void *
voids all warranty (pun not intended).到其他指针的中间转换,如void *
会使所有保修失效(双关语不是有意的)。
void* a = new Derived();
auto b = (Interface*)a;
This is Undefined Behavior .这是未定义的行为。 You are not casting the void*
back to the same type that was stored in it ( Derived*
).您没有将void*
转换回存储在其中的相同类型( Derived*
)。
If you want to cast the void*
to Interface*
then you need to store an Interface*
in the void*
to begin with, eg:如果要将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 call. 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称呼。
The Derived
object looks something like this in memory (details may vary by compiler, but this is the gist of it 1 ): 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()
+------------+
A Derived
object consists of everything in Base
, Interface
and Derived
cobbled together. Derived
object 由Base
、 Interface
和Derived
拼凑在一起的所有内容组成。 Each class still has its own pointer to a virtual method table belonging to that class.每个 class 仍然有自己的指向属于该 class 的虚拟方法表的指针。 But since Derived
implements all of the virtual
methods, a Derived
object has multiple vtable pointers, and they all refer to Derived
's methods.但是由于Derived
实现了所有的virtual
方法,一个Derived
object 有多个 vtable 指针,它们都引用了Derived
的方法。
1: for efficiency, it is likely that Derived
only has 1 vtable and all 3 of its vmt pointers are pointing to different areas of that single table. 1:为了提高效率, Derived
很可能只有 1 个 vtable,并且它的所有 3 个 vmt 指针都指向该单个表的不同区域。 But that is an implementation detail, not important for purposes of this answer.但这是一个实现细节,对于这个答案来说并不重要。
When a
is declared as Interface*
, it points at the Interface
portion of the Derived
object:当a
被声明为Interface*
时,它指向Derived
object 的Interface
部分:
+---------------+
| Base vmt |
|---------------|
a -> | Interface vmt |
|---------------|
| Derived vmt |
+---------------+
Type-casting a
to Interface*
is effectively a no-op, resulting in b
being an Interface*
pointer to the Interface
portion of the Derived
object:将a
类型转换为Interface*
实际上是一个无操作,导致b
是一个Interface*
指针,指向Derived
object 的Interface
部分:
+---------------+
| Base vmt |
|---------------|
b -> | Interface vmt |
|---------------|
| Derived vmt |
+---------------+
So calling b->func2()
uses Interface'
s vtable, jumping to 2nd entry which is Derived::func2()
, as expected.因此调用b->func2()
使用Interface'
的 vtable,如预期的那样跳转到Derived::func2()
的第二个条目。
When a
is declared as Base*
, it points at the Base
portion of the Derived
object:当a
被声明为Base*
时,它指向Derived
object 的Base
部分:
+---------------+
a -> | Base vmt |
|---------------|
| Interface vmt |
|---------------|
| Derived vmt |
+---------------+
But type-casting a
to Interface*
is Undefined Behavior , because Base
and Interface
are not related to each other, resulting in b
being an Interface*
pointer to the Base
portion of the Derived
object:但是将a
类型转换为Interface*
是Undefined Behavior ,因为Base
和Interface
彼此不相关,导致b
是Interface*
指向Derived
object 的Base
部分的指针:
+---------------+
b -> | Base vmt |
|---------------|
| Interface vmt |
|---------------|
| Derived vmt |
+---------------+
So calling b->func2()
mistakenly uses Base
's vtable instead of Interface
's vtable, jumping to the 2nd entry which is Derived::func1()
, unexpectedly.因此调用b->func2()
错误地使用Base
的 vtable 而不是Interface
的 vtable,意外地跳转到第二个条目Derived::func1()
。
Had you used static_cast
instead of a C-style cast here, the compile would have simply failed instead (this is why you should avoid using C-style casts in C++.).如果你在这里使用static_cast
而不是 C 风格的转换,编译就会失败(这就是为什么你应该避免在 C++ 中使用 C 风格的转换。)。
When a
is declared as Derived*
, it points at the Derived
portion of the Derived
object:当a
被声明为Derived*
时,它指向Derived
object 的Derived
部分:
+---------------+
| Base vmt |
|---------------|
| Interface vmt |
|---------------|
a -> | Derived vmt |
+---------------+
Type-casting a
to Interface*
is well-defined since Interface
is a base of Derived
, resulting in b
being an Interface*
pointer to the Interface
portion of the Derived
object:将a
类型转换为Interface*
是明确定义的,因为Interface
是Derived
的基础,导致b
是指向Derived
object 的Interface
部分的Interface*
指针:
+---------------+
| Base vmt |
|---------------|
b -> | Interface vmt |
|---------------|
| Derived vmt |
+---------------+
So calling b->func2()
uses Interface
's vtable, jumping to the 2nd entry which is Derived::func2()
, as expected.因此调用b->func2()
使用Interface
的 vtable,如预期的那样跳转到Derived::func2()
的第二个条目。
When a
is declared as void*
, it points at the Derived
portion of the Derived
object:当a
被声明为void*
时,它指向Derived
object 的Derived
部分:
+---------------+
| Base vmt |
|---------------|
| Interface vmt |
|---------------|
a -> | Derived vmt |
+---------------+
But type-casting a
to Interface*
is Undefined Behavior , because a
is not pointing at the Interface
portion of the Derived
object, resulting in an Interface*
pointer to the Derived
portion of the Derived
object:但是将a
类型转换为Interface*
是Undefined Behavior ,因为a
没有指向Derived
object 的Interface
部分,从而导致Interface*
指针指向Derived
object 的Derived
部分:
+---------------+
| Base vmt |
|---------------|
| Interface vmt |
|---------------|
b -> | Derived vmt |
+---------------+
So calling b->func2()
mistakenly uses Derived
's vtable instead of Interface
's vtable, jumping to the 2nd entry which is Derived::func1()
, unexpectedly.因此调用b->func2()
错误地使用Derived
的 vtable 而不是Interface
的 vtable,意外地跳转到第二个条目Derived::func1()
。
Behaviour is undefined, since you are not converting a void *
back to the actual type.行为未定义,因为您没有将void *
转换回实际类型。
You either need to do你要么需要做
void *a = (Interface*)(new Derived());
auto b = (Interface *)a;
or add an extra step between your two或者在你们两个之间增加一个额外的步骤
void* a = new Derived();
auto temp = (Derived *)a; // explicitly convert the void pointer to the actual type
auto b = (Interface*)temp;
which is equivalent to这相当于
void* a = new Derived();
auto b = (Interface*)((Derived *)a);
Either way, without the conversion of the void *
to Derived *
before converting to Interface *
, the behaviour is undefined.无论哪种方式,如果在转换为Interface *
之前没有将void *
转换为Derived *
,则行为是未定义的。
Consider using one of the _cast
s (eg static_cast
) for conversion instead of C-style casts as well.考虑使用_cast
之一(例如static_cast
)进行转换,而不是使用 C 风格的强制转换。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.