[英]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.