简体   繁体   English

多个 inheritance 订单重要吗?

[英]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 由BaseInterfaceDerived拼凑在一起的所有内容组成。 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 ,因为BaseInterface彼此不相关,导致bInterface*指向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*是明确定义的,因为InterfaceDerived的基础,导致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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM