簡體   English   中英

多個 inheritance 訂單重要嗎?

[英]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 由BaseInterfaceDerived拼湊在一起的所有內容組成。 每個 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 ,因為BaseInterface彼此不相關,導致bInterface*指向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*是明確定義的,因為InterfaceDerived的基礎,導致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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM