虚拟多重继承-最终替代者 [英] Virtual multiple inheritance - final overrider

查看:62
本文介绍了虚拟多重继承-最终替代者的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在尝试更深入地分析C ++的继承机制时,我偶然发现了以下示例:

while trying to analyse in greater depth inheritance mechanism of C++ I stumbled upon the following example:

#include<iostream>

using namespace std;

class Base {
public:
    virtual void f(){
    cout << "Base.f" << endl; 
    }
};

class Left : public virtual Base {    
};

class Right : public virtual Base{
public:
    virtual void f(){
        cout << "Right.f" << endl; 
    }
};

class Bottom : public Left, public Right{

};

int main(int argc,char **argv)
{
    Bottom* b = new Bottom();
    b->f();
}

以上内容以某种方式编译并调用Right :: f().我了解了编译器中可能发生的情况,它了解到有一个共享的Base对象,并且Right覆盖了f(),但实际上,据我了解,应该有两种方法:Left::f()(从<继承c1>)和Right::f(),它们会覆盖Base::f().现在,我想,基于Bottom继承的两个单独的方法,它们都具有相同的签名,应该存在冲突.

The above, somehow, compiles and calls Right::f(). I see what might be going on in the compiler, that it understands that there is one shared Base object, and that Right overrides f(), but really, in my understanding, there should be two methods: Left::f() (inherited from Base::f()) and Right::f(), which overrides Base::f(). Now, I would think, based that there are two separate methods being inherited by Bottom, both with same signature, there should be a clash.

任何人都可以从低层次的角度解释C ++的哪些规范细节以及如何处理这种情况吗?

Could anyone explain which specification detail of C++ deals with this case and how it does it from the low-level perspective?

推荐答案

可怕的钻石中,存在一个单一的基,两个中间对象均源自该基,然后第四个类型使用以下方法封闭钻石在中间级别,两种类型都有多重继承.

In the dreaded diamond there is a single base, from which the two intermediate objects derive and then the fourth type closes the diamond with multiple inheritance from both types in the intermediate levels.

您的问题似乎是在上一个示例中声明了多少f个函数?,答案是一个.

Your question seems to be how many f functions are declared in the previous example? and the answer is one.

让我们从简单的仅基于基本和派生的线性层次结构示例开始:

Lets start with the simpler example of a linear hierarchy of just base and derived:

struct base {
   virtual void f() {}
};
struct derived : base {
   virtual void f() {}
};

在此示例中,声明了一个f,其中有两个覆盖,即base::fderived::f.在类型为derived的对象中,最终的替代程序为derived::f.重要的是要注意,两个f函数都代表具有多个实现的单个函数.

In this example there is a single f declared for which there are two overrides, base::f and derived::f. In an object of type derived, the final overrider is derived::f. It is important to note that both f functions represent a single function that has multiple implementations.

现在,回到原始示例,在右侧的行中,Base::fRight::f以相同的方式替代了相同的函数.因此,对于类型为Right的对象,最终的替代项为Right::f.现在,对于类型为Left的最终对象,最终的替代程序为Base::f,因为Left不会替代该功能.

Now, going back to the original example, on the line on the right, Base::f and Right::f are in the same way the same function that is overridden. So for an object of type Right, the final overrider is Right::f. Now for a final object of type Left, the final overrider is Base::f as Left does not override the function.

当菱形关闭时,并且由于继承是virtual,因此只有一个Base对象,该对象声明了一个f函数.在继承的第二级中,Right使用其自己的实现重写该函数,并且这是对派生程度最高的类型Bottom的最终重写器.

When the diamond is closed, and because inheritance is virtual there is a single Base object, that declares a single f function. In the second level of inheritance, Right overrides that function with its own implementation and that is the final overrider for the most derived type Bottom.

您可能想看看标准之外的内容,并看看编译器实际上是如何实现的.编译器在创建Base对象时会向虚拟表添加隐藏的指针vptr.虚拟表包含指向 thunks 的指针(为简单起见,仅假设该表包含指向函数的最终替代程序[1]的指针).在这种情况下,Base对象将不包含成员数据,而仅包含指向表的指针,该表包含指向函数Base::f的指针.

You might want to look at this outside of the standard and take a look at how this is actually implemented by compilers. The compiler, when creating the Base object it adds a hidden pointer vptr to the virtual table. The virtual table holds pointers to thunks (for simplicity just assume that the table held pointers to the function's final overriders, [1]). In this case, the Base object will contain no member data and just a pointer to a table that holds a pointer to the function Base::f.

Left扩展Base时,将为Left创建一个新的vtable,并且该vtable中的指针在此级别上设置为f的最终替代程序,偶然是Base::f,因此指针在两个vtable中(忽略Trapolpolin)跳转到相同的实际实现.当构造类型为Left的对象时,首先会初始化Base子对象,然后在初始化Left的成员(如果有)之前,将Base::vptr指针更新为引用(即存储在Base中的指针是指为Left定义的表).

When Left extends Base, a new vtable is created for Left and the pointer in that vtable is set to the final overrider of f at this level, which is incidentally Base::f so the pointers in both vtables (ignoring the trampolin) jump to the same actual implementation. When an object of type Left is being constructed, the Basesubobject is initialized first, and then prior to initialization of the members of Left (if there were) the Base::vptr pointer is updated to refer to Left::vtable (i.e. the pointer stored in Base refers to the table defined for Left).

在菱形的另一侧,为Right创建的vtable包含单个 thunk ,最终调用了Right::f.如果要创建类型为Right的对象,则将执行相同的初始化过程,并且Base::vptr将指向Derived::f.

On the other side of the diamond, the vtable that is created for Right contains a single thunk that ends up calling Right::f. If an object of type Right was to be created the same initialization process would happen and the Base::vptr would point to Derived::f.

现在,我们进入最终对象Bottom.同样,将为类型Bottom生成一个vtable,并且与其他所有vtable一样,该vtable包含一个表示f的条目.编译器分析继承的层次结构,并确定Right::f覆盖Base::f,并且在左分支上没有等效的覆盖,因此在Bottom的vtable中,表示f的指针引用了Right::f.同样,在构造Bottom对象的过程中,Base::vptr被更新为引用Bottom的vtable.

Now we get to the final object Bottom. Again, a vtable is generated for the type Bottom and that vtable, as is the case in all others, contains a single entry that represents f. The compiler analyzes the hierarchy of inheritance and determines that Right::f overrides Base::f, and there is no equivalent override on the left branch, so in Bottom's vtable the pointer representing f refers to Right::f. Again, during construction of the Bottom object, the Base::vptr is updated to refer to Bottom's vtable.

如您所见,所有四个vtable都有一个f条目,即使每个vtable中存储的值不同,程序中也会有一个单个 f替代者不同).

As you see, all four vtables have a single entry for f, there is a single f in the program, even if the value stored in each vtable is different (the final overriders differ).

[1] thunk 是一小段代码,可根据需要调整this指针(通常需要多个继承),然后将调用转发给实际的替代.如果是单继承,则不需要更新this指针,并且 thunk 消失,vtable中的条目直接指向实际函数.

[1] The thunk is a small piece of code that adapts the this pointer if needed (multiple inheritance usually implies it is needed) and then forwards the call to the actual override. In the event of single inheritance, the this pointer does not need to be updated and the thunk disappears, with the entry in the vtable pointing directly to the actual function.

这篇关于虚拟多重继承-最终替代者的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆