在C ++中,什么是虚拟基类?


403

我想知道什么是“ 虚拟基类 ”及其含义。

让我举一个例子:

class Foo
{
public:
    void DoSomething() { /* ... */ }
};

class Bar : public virtual Foo
{
public:
    void DoSpecific() { /* ... */ }
};

我们是否应该在“多重继承”中使用虚拟基类,因为如果类A具有成员变量int a,而类B也具有成员int a,并且类c继承了类A和B,我们如何决定使用哪个“ a”?
纳米特·辛哈

2
@NamitSinha没有,虚拟继承并没有解决这个问题。无论如何,成员a都是模棱两可的
Ichthyo

@NamitSinha虚拟继承并不是消除多重继承相关歧义的神奇工具。它“解决”了一个或多个具有间接基础的“问题”。如果打算共享它,这只是一个问题(通常但并非总是如此)。
curiousguy19年

Answers:


533

虚拟继承中使用的虚拟基类是一种防止使用多重继承时给定类的多个“实例”出现在继承层次结构中的方法。

请考虑以下情形:

class A { public: void Foo() {} };
class B : public A {};
class C : public A {};
class D : public B, public C {};

上面的类层次结构导致“可怕的菱形”看起来像这样:

  A
 / \
B   C
 \ /
  D

D的实例将由B(包括A)和C(其中也包括A)组成。因此,您有A的两个“实例”(为了更好地表达)。

当您遇到这种情况时,就有可能产生歧义。当您这样做时会发生什么:

D d;
d.Foo(); // is this B's Foo() or C's Foo() ??

虚拟继承可以解决这个问题。在继承类时指定virtual时,就是在告诉编译器只需要一个实例。

class A { public: void Foo() {} };
class B : public virtual A {};
class C : public virtual A {};
class D : public B, public C {};

这意味着层次结构中仅包含A的一个“实例”。因此

D d;
d.Foo(); // no longer ambiguous

这是一个简短的摘要。有关更多信息,请阅读thisthis这里也有一个很好的例子。


7
@Bohdan不,它不是:)
OJ。

6
@OJ。为什么不?他们很搞笑:)
Bohdan

15
@Bohdan尽可能少地使用虚拟关键字,因为当我们使用虚拟关键字时,会应用较重的机制。因此,您的程序效率将降低。
萨加尔2014年

73
您的“可怕的钻石”图看起来很混乱,尽管它似乎很常用。这实际上是一个显示类继承关系的图,而不是对象布局。令人困惑的是,如果我们使用virtual,则对象布局看起来像菱形;如果不使用,virtual则对象布局看起来像是包含两个As 的树结构
MM

5
由于MM概述的原因,我不得不对此答案投下反对票-该图表达了与帖子相反的内容。
大卫·斯通

251

关于内存布局

附带说明一下,Dreaded Diamond的问题在于基类存在多次。因此,通过常规继承,您相信自己具有:

  A
 / \
B   C
 \ /
  D

但是在内存布局中,您有:

A   A
|   |
B   C
 \ /
  D

这解释了为什么在通话时D::foo(),您有歧义性问题。但是,当您想使用的成员变量时,真正的问题就来了A。例如,假设我们有:

class A
{
    public :
       foo() ;
       int m_iValue ;
} ;

当您尝试从访问m_iValueD,编译器会提出抗议,因为在层次结构中,它将看到两个m_iValue,而不是一个。而且,如果您修改一个,例如B::m_iValue(是的A::m_iValue父项B),C::m_iValue则不会被修改(这是的A::m_iValue父项C)。

在这里,虚拟继承很方便,与之类似,您将回到真正的菱形布局,不仅使用一种foo()方法,而且还使用一种方法,并且只有一种方法m_iValue

可能出什么问题了?

想像:

  • A 具有一些基本功能。
  • B 向其中添加一些很酷的数据数组(例如)
  • C向其中添加了一些很酷的功能,例如观察者模式(例如on m_iValue)。
  • D从继承BC,并且因此从A

对于正常的继承,m_iValueD进行修改是不明确的,必须解决。即使是这样,有两个m_iValues里面D,所以你最好记住,并在同一时间更新这两个。

使用虚拟继承,可以m_iValueD进行修改...但是...假设您有D。通过其C界面,您连接了一个观察者。通过其B界面,您可以更新很酷的数组,它的副作用是直接更改m_iValue...

由于更改m_iValue是直接完成的(不使用虚拟访问器方法),因此C不会调用观察者“监听” ,因为实现监听的代码在中C,并且B对此一无所知。

结论

如果您的层次结构中有钻石,则意味着您有95%的概率对上述层次结构做错了事。


您的“可能出什么问题”是由于直接访问基本成员,而不是由于多重继承。摆脱“ B”,您将遇到相同的问题。基本规则:“如果不是私有的,则应该是虚拟的”避免了问题。m_iValue不是虚拟的,因此应该是私有的
Chris Dodd

4
@克里斯·多德:不完全是。m_iValue会发生任何符号变化(例如,typedef,成员变量,成员函数,强制转换为基类等)。这确实是一个多重继承问题,用户应该注意正确使用多重继承,而不是遵循Java的方式得出结论:“多重继承是100%邪恶的,让我们使用接口来做到这一点”。
paercebal

嗨,当我们使用虚拟关键字时,将只有A的一个副本。我的​​问题是我们如何知道它是来自B还是C?我的问题完全有效吗?
user875036 2014年

@ user875036:A既来自B又来自C。实际上,虚拟性改变了一些事情(例如D将调用A的构造函数,而不是B或C)。B和C(以及d)具有一个指向A.
paercebal

3
FWIW,如果有人想知道,成员变量不能是虚拟的-虚拟是函数的说明符。SO参考:stackoverflow.com/questions/3698831/...
rholmes

34

用虚拟基础解释多重继承需要了解C ++对象模型。最好在文章中而不是在评论框中清楚地说明主题。

我发现可以解决我对此问题的所有疑问的最好的可读性解释是这篇文章:http : //www.phpcompiler.org/articles/virtualinheritance.html

阅读完该主题之后,您真的不需要阅读其他任何相关主题(除非您是编译器作者)...


10

虚拟基类是无法实例化的类:您无法从中创建直接对象。

我认为您混淆了两个截然不同的事物。虚拟继承与抽象类不是一回事。虚拟继承修改了函数调用的行为。有时,它解决了否则可能会造成歧义的函数调用;有时,它将函数调用处理推迟到了非虚拟继承所期望的类之外的其他类。


7

我想补充一下OJ的种类说明。

虚拟继承并非没有代价。就像虚拟的所有事物一样,您会受到性能的影响。解决这种性能问题的方法可能不太优雅。

无需通过虚拟衍生来破坏钻石,您可以在钻石上添加另一层,以获得如下所示的内容:

   B
  / \
D11 D12
 |   |
D21 D22
 \   /
  DD

没有一个类是虚拟继承的,所有类都是公开继承的。然后,类D21和D22将隐藏对于DD不明确的虚函数f(),也许是通过将函数声明为私有的。他们分别定义了一个包装函数f1()和f2(),每个函数都调用类本地(私有)f(),从而解决了冲突。类DD如果需要D11 :: f()则调用f1(),如果需要D12 :: f()则调用f2()。如果您内联定义包装器,则可能会获得大约零开销。

当然,如果您可以更改D11和D12,则可以在这些类中执行相同的操作,但通常并非如此。


2
这或多或少不是优雅的,也不是解决歧义的问题(您可以始终使用明确的xxx ::规范)。对于非虚拟继承,类DD的每个实例都具有两个独立的B实例。只要该类具有单个非静态数据成员,虚拟和非虚拟继承的区别就不仅仅是语法。
user3489112 2014年

@ user3489112 ...一无所有。虚拟继承和非虚拟继承在语义上是不同的。
curiousguy


1

你有点困惑。我不知道您是否混淆了一些概念。

您的OP中没有虚拟基类。您只有一个基类。

您进行了虚拟继承。通常在多重继承中使用它,以便多个派生类使用基类的成员而不重现它们。

具有纯虚函数的基类不会被实例化。这需要Paul掌握的语法。通常使用它,以便派生类必须定义这些函数。

我不想再对此进行解释,因为我无法完全理解您的要求。


1
在虚拟继承中使用的“基类”成为“虚拟基类”(在该精确继承的上下文中)。
Luc Hermitte

1

这意味着对虚函数的调用将被转发到“正确”类。

C ++ 常见问题精简版 FTW。

简而言之,它通常用于形成“钻石”层次结构的多继承方案中。当您在该类中调用函数时,虚拟继承将打破在底层类中创建的歧义,并且该函数需要解析为该底层类之上的D1或D2类。有关图表和详细信息,请参见FAQ项目

它在姐妹代表团中也使用,这是一个强大的功能(尽管不是胆小的人)。请参阅常见问题解答。

另请参见有效C ++第三版(第二版43)中的第40项。


1

Diamond继承可运行用法示例

本示例说明如何在典型方案中使用虚拟基类:解决菱形继承。

#include <cassert>

class A {
    public:
        A(){}
        A(int i) : i(i) {}
        int i;
        virtual int f() = 0;
        virtual int g() = 0;
        virtual int h() = 0;
};

class B : public virtual A {
    public:
        B(int j) : j(j) {}
        int j;
        virtual int f() { return this->i + this->j; }
};

class C : public virtual A {
    public:
        C(int k) : k(k) {}
        int k;
        virtual int g() { return this->i + this->k; }
};

class D : public B, public C {
    public:
        D(int i, int j, int k) : A(i), B(j), C(k) {}
        virtual int h() { return this->i + this->j + this->k; }
};

int main() {
    D d = D(1, 2, 4);
    assert(d.f() == 3);
    assert(d.g() == 5);
    assert(d.h() == 7);
}

2
assert(A::aDefault == 0);从主要功能给我一个编译错误:aDefault is not a member of A使用gcc 5.4.0。应该怎么办?
SebNag

@SebTu啊,谢谢,只是我忘了从复制粘贴中删除的内容,现在将其删除。没有它,该示例仍然应该有意义。
Ciro Santilli郝海东冠状病六四事件法轮功

0

虚拟类是一样的虚拟继承。您无法实例化的虚拟类,虚拟继承完全是另外一回事。

维基百科比我能更好地描述它。http://en.wikipedia.org/wiki/Virtual_inheritance


6
C ++中没有“虚拟类”之类的东西。但是,对于给定的继承,存在“虚拟”的“虚拟基类”。您所指的是正式称为“抽象类”的东西。
卢克·赫米特

@ LucHermitte,C ++中肯定有虚拟类。进行检查:en.wikipedia.org/wiki/Virtual_class
拉菲德2014年

“错误:只能为功能指定'虚拟'”。我不知道这是什么语言。但是,绝对没有C ++中的虚类
Luc Hermitte

0

常规继承

对于典型的3级非钻石非虚拟继承,当实例化一个新的最派生对象时,将调用new,并且编译器根据类类型解析该对象所需的大小并将其传递给new。

new有签名:

_GLIBCXX_WEAK_DEFINITION void *
operator new (std::size_t sz) _GLIBCXX_THROW (std::bad_alloc)

并调用malloc,返回void指针

然后将其传递给派生程度最高的对象的构造函数,该对象将立即调用中间构造函数,然后中间构造函数将立即调用基本构造函数。然后,基础在对象的开头存储指向其虚拟表的指针,然后在其之后存储其属性。然后返回到中间构造函数,该构造函数会将其虚拟表指针存储在相同位置,然后将其属性存储在基本构造函数将存储的属性之后。它返回到最派生的构造函数,该构造函数将指针存储在相同位置的虚拟表,然后将其属性存储在中间构造函数应存储的属性之后。

由于虚拟表指针被覆盖,因此虚拟表指针最终始终是派生程度最高的类之一。虚拟性会向最派生的类传播,因此,如果函数在中产类中是虚拟的,那么它将在最派生类中而不是基类中是虚拟的。如果将大多数派生类的实例多态转换为指向基类的指针,则编译器不会将其解析为对虚拟表的间接调用,而是直接调用该函数A::function()。如果函数是针对您所转换类型的虚拟函数,则它将解析为对虚拟表的调用,该调用始终是最派生类的调用。如果该类型不是虚拟的,则它将调用Type::function()并传递对象指针,并将其传递给Type。

实际上,当我说到指向其虚拟表的指针时,它实际上总是在虚拟表中偏移16。

vtable for Base:
        .quad   0
        .quad   typeinfo for Base
        .quad   Base::CommonFunction()
        .quad   Base::VirtualFunction()

pointer is typically to the first function i.e. 

        mov     edx, OFFSET FLAT:vtable for Base+16

virtual如果在派生程度较低的类中是虚拟的,则它在更高派生的类中不再是必需的,因为它会传播。但是它可以用来表明该函数确实是一个虚函数,而不必检查它继承的类型定义的类。

override 是另一个编译器防护,它表示此函数将覆盖某些内容,如果没有覆盖,则会引发编译器错误。

= 0 表示这是一个抽象函数

final 防止在更多派生类中再次实现虚函数,并确保最派​​生类的虚拟表包含该类的最终函数。

= default 在文档中明确指出编译器将使用默认实现

= delete 如果尝试对此调用给出编译器错误

虚拟继承

考虑

class Base
  {
      int a = 1;
      int b = 2;
  public:
      void virtual CommonFunction(){} ;
      void virtual VirtualFunction(){} ;
  };


class DerivedClass1: virtual public Base
  {
      int c = 3;
  public:
    void virtual DerivedCommonFunction(){} ;
     void virtual VirtualFunction(){} ;
  };

  class DerivedClass2 : virtual public Base
 {
     int d = 4;
 public:
     //void virtual DerivedCommonFunction(){} ;    
     void virtual VirtualFunction(){} ;
     void virtual DerivedCommonFunction2(){} ;
 };

class DerivedDerivedClass :  public DerivedClass1, public DerivedClass2
 {
   int e = 5;
 public:
     void virtual DerivedDerivedCommonFunction(){} ;
     void virtual VirtualFunction(){} ;
 };

 int main () {
   DerivedDerivedClass* d = new DerivedDerivedClass;
   d->VirtualFunction();
   d->DerivedCommonFunction();
   d->DerivedCommonFunction2();
   d->DerivedDerivedCommonFunction();
   ((DerivedClass2*)d)->DerivedCommonFunction2();
   ((Base*)d)->VirtualFunction();
 }

在不虚拟继承低音类的情况下,您将获得一个看起来像这样的对象:

代替这个:

即将有2个基础对象。

在上面的虚拟菱形继承的情况下,新的被称为后,它会调用最派生的构造函数,并在构造函数中,它调用的所有3派生的构造函数传递偏移到其虚表的表,而不是调用只是打电话DerivedClass1::DerivedClass1()DerivedClass2::DerivedClass2(),然后将这些都呼Base::Base()

以下都是在调试模式-O0下编译的,因此会有多余的程序集

main:
.LFB8:
        push    rbp
        mov     rbp, rsp
        push    rbx
        sub     rsp, 24
        mov     edi, 48 //pass size to new
        call    operator new(unsigned long) //call new
        mov     rbx, rax  //move the address of the allocation to rbx
        mov     rdi, rbx  //move it to rdi i.e. pass to the call
        call    DerivedDerivedClass::DerivedDerivedClass() [complete object constructor] //construct on this address
        mov     QWORD PTR [rbp-24], rbx  //store the address of the object on the stack as d
DerivedDerivedClass::DerivedDerivedClass() [complete object constructor]:
.LFB20:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     QWORD PTR [rbp-8], rdi
.LBB5:
        mov     rax, QWORD PTR [rbp-8] // object address now in rax 
        add     rax, 32 //increment address by 32
        mov     rdi, rax // move object address+32 to rdi i.e. pass to call
        call    Base::Base() [base object constructor]
        mov     rax, QWORD PTR [rbp-8] //move object address to rax
        mov     edx, OFFSET FLAT:VTT for DerivedDerivedClass+8 //move address of VTT+8 to edx
        mov     rsi, rdx //pass VTT+8 address as 2nd parameter 
        mov     rdi, rax //object address as first
        call    DerivedClass1::DerivedClass1() [base object constructor]
        mov     rax, QWORD PTR [rbp-8] //move object address to rax
        add     rax, 16  //increment object address by 16
        mov     edx, OFFSET FLAT:VTT for DerivedDerivedClass+24  //store address of VTT+24 in edx
        mov     rsi, rdx //pass address of VTT+24 as second parameter
        mov     rdi, rax //address of object as first
        call    DerivedClass2::DerivedClass2() [base object constructor]
        mov     edx, OFFSET FLAT:vtable for DerivedDerivedClass+24 //move this to edx
        mov     rax, QWORD PTR [rbp-8] // object address now in rax
        mov     QWORD PTR [rax], rdx. //store address of vtable for DerivedDerivedClass+24 at the start of the object
        mov     rax, QWORD PTR [rbp-8] // object address now in rax
        add     rax, 32  // increment object address by 32
        mov     edx, OFFSET FLAT:vtable for DerivedDerivedClass+120 //move this to edx
        mov     QWORD PTR [rax], rdx  //store vtable for DerivedDerivedClass+120 at object+32 (Base) 
        mov     edx, OFFSET FLAT:vtable for DerivedDerivedClass+72 //store this in edx
        mov     rax, QWORD PTR [rbp-8] //move object address to rax
        mov     QWORD PTR [rax+16], rdx //store vtable for DerivedDerivedClass+72 at object+16 (DerivedClass2)
        mov     rax, QWORD PTR [rbp-8]
        mov     DWORD PTR [rax+28], 5
.LBE5:
        nop
        leave
        ret

Base::Base()使用指向对象偏移量32的指针进行调用。Base将指向其虚拟表的指针存储在其接收的地址及其后的成员中。

Base::Base() [base object constructor]:
.LFB11:
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], rdi //stores address of object on stack (-O0)
.LBB2:
        mov     edx, OFFSET FLAT:vtable for Base+16  //puts vtable for Base+16 in edx
        mov     rax, QWORD PTR [rbp-8] //copies address of object from stack to rax
        mov     QWORD PTR [rax], rdx  //stores it address of object
        mov     rax, QWORD PTR [rbp-8] //copies address of object on stack to rax again
        mov     DWORD PTR [rax+8], 1 //stores a = 1 in the object
        mov     rax, QWORD PTR [rbp-8] //junk from -O0
        mov     DWORD PTR [rax+12], 2  //stores b = 2 in the object
.LBE2:
        nop
        pop     rbp
        ret

DerivedDerivedClass::DerivedDerivedClass()然后DerivedClass1::DerivedClass1()使用指向对象偏移量0的指针进行调用,并传递VTT for DerivedDerivedClass+8

DerivedClass1::DerivedClass1() [base object constructor]:
.LFB14:
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], rdi //address of object
        mov     QWORD PTR [rbp-16], rsi  //address of VTT+8
.LBB3:
        mov     rax, QWORD PTR [rbp-16]  //address of VTT+8 now in rax
        mov     rdx, QWORD PTR [rax]     //address of DerivedClass1-in-DerivedDerivedClass+24 now in rdx
        mov     rax, QWORD PTR [rbp-8]   //address of object now in rax
        mov     QWORD PTR [rax], rdx     //store address of DerivedClass1-in-.. in the object
        mov     rax, QWORD PTR [rbp-8]  // address of object now in rax
        mov     rax, QWORD PTR [rax]    //address of DerivedClass1-in.. now implicitly in rax
        sub     rax, 24                 //address of DerivedClass1-in-DerivedDerivedClass+0 now in rax
        mov     rax, QWORD PTR [rax]    //value of 32 now in rax
        mov     rdx, rax                // now in rdx
        mov     rax, QWORD PTR [rbp-8]  //address of object now in rax
        add     rdx, rax                //address of object+32 now in rdx
        mov     rax, QWORD PTR [rbp-16]  //address of VTT+8 now in rax
        mov     rax, QWORD PTR [rax+8]   //address of DerivedClass1-in-DerivedDerivedClass+72 (Base::CommonFunction()) now in rax
        mov     QWORD PTR [rdx], rax     //store at address object+32 (offset to Base)
        mov     rax, QWORD PTR [rbp-8]  //store address of object in rax, return
        mov     DWORD PTR [rax+8], 3    //store its attribute c = 3 in the object
.LBE3:
        nop
        pop     rbp
        ret
VTT for DerivedDerivedClass:
        .quad   vtable for DerivedDerivedClass+24
        .quad   construction vtable for DerivedClass1-in-DerivedDerivedClass+24
        .quad   construction vtable for DerivedClass1-in-DerivedDerivedClass+72
        .quad   construction vtable for DerivedClass2-in-DerivedDerivedClass+24
        .quad   construction vtable for DerivedClass2-in-DerivedDerivedClass+72
        .quad   vtable for DerivedDerivedClass+120
        .quad   vtable for DerivedDerivedClass+72

construction vtable for DerivedClass1-in-DerivedDerivedClass:
        .quad   32
        .quad   0
        .quad   typeinfo for DerivedClass1
        .quad   DerivedClass1::DerivedCommonFunction()
        .quad   DerivedClass1::VirtualFunction()
        .quad   -32
        .quad   0
        .quad   -32
        .quad   typeinfo for DerivedClass1
        .quad   Base::CommonFunction()
        .quad   virtual thunk to DerivedClass1::VirtualFunction()
construction vtable for DerivedClass2-in-DerivedDerivedClass:
        .quad   16
        .quad   0
        .quad   typeinfo for DerivedClass2
        .quad   DerivedClass2::VirtualFunction()
        .quad   DerivedClass2::DerivedCommonFunction2()
        .quad   -16
        .quad   0
        .quad   -16
        .quad   typeinfo for DerivedClass2
        .quad   Base::CommonFunction()
        .quad   virtual thunk to DerivedClass2::VirtualFunction()
vtable for DerivedDerivedClass:
        .quad   32
        .quad   0
        .quad   typeinfo for DerivedDerivedClass
        .quad   DerivedClass1::DerivedCommonFunction()
        .quad   DerivedDerivedClass::VirtualFunction()
        .quad   DerivedDerivedClass::DerivedDerivedCommonFunction()
        .quad   16
        .quad   -16
        .quad   typeinfo for DerivedDerivedClass
        .quad   non-virtual thunk to DerivedDerivedClass::VirtualFunction()
        .quad   DerivedClass2::DerivedCommonFunction2()
        .quad   -32
        .quad   0
        .quad   -32
        .quad   typeinfo for DerivedDerivedClass
        .quad   Base::CommonFunction()
        .quad   virtual thunk to DerivedDerivedClass::VirtualFunction()

virtual thunk to DerivedClass1::VirtualFunction():
        mov     r10, QWORD PTR [rdi]
        add     rdi, QWORD PTR [r10-32]
        jmp     .LTHUNK0
virtual thunk to DerivedClass2::VirtualFunction():
        mov     r10, QWORD PTR [rdi]
        add     rdi, QWORD PTR [r10-32]
        jmp     .LTHUNK1
virtual thunk to DerivedDerivedClass::VirtualFunction():
        mov     r10, QWORD PTR [rdi]
        add     rdi, QWORD PTR [r10-32]
        jmp     .LTHUNK2
non-virtual thunk to DerivedDerivedClass::VirtualFunction():
        sub     rdi, 16
        jmp     .LTHUNK3

        .set    .LTHUNK0,DerivedClass1::VirtualFunction()
        .set    .LTHUNK1,DerivedClass2::VirtualFunction()
        .set    .LTHUNK2,DerivedDerivedClass::VirtualFunction()
        .set    .LTHUNK3,DerivedDerivedClass::VirtualFunction()

DerivedDerivedClass::DerivedDerivedClass()然后将object + 16的地址和VTT的地址DerivedDerivedClass+24传递给DerivedClass2::DerivedClass2()其,其汇编程序与之相同,DerivedClass1::DerivedClass1()除了mov DWORD PTR [rax+8], 3明显带有4而不是3的行d = 4

之后,它将对象中的所有3个虚拟表指针替换为指向DerivedDerivedClass该类表示形式的vtable中的偏移量的指针。

d->VirtualFunction();

        mov     rax, QWORD PTR [rbp-24] //store pointer to virtual table in rax 
        mov     rax, QWORD PTR [rax] //dereference and store in rax
        add     rax, 8 // call the 2nd function in the table
        mov     rdx, QWORD PTR [rax] //dereference 
        mov     rax, QWORD PTR [rbp-24]
        mov     rdi, rax
        call    rdx

d->DerivedCommonFunction();

        mov     rax, QWORD PTR [rbp-24]
        mov     rdx, QWORD PTR [rbp-24]
        mov     rdx, QWORD PTR [rdx]
        mov     rdx, QWORD PTR [rdx]
        mov     rdi, rax
        call    rdx

d->DerivedCommonFunction2();

        mov     rax, QWORD PTR [rbp-24]
        lea     rdx, [rax+16]
        mov     rax, QWORD PTR [rbp-24]
        mov     rax, QWORD PTR [rax+16]
        add     rax, 8
        mov     rax, QWORD PTR [rax]
        mov     rdi, rdx
        call    rax

d->DerivedDerivedCommonFunction();

        mov     rax, QWORD PTR [rbp-24]
        mov     rax, QWORD PTR [rax]
        add     rax, 16
        mov     rdx, QWORD PTR [rax]
        mov     rax, QWORD PTR [rbp-24]
        mov     rdi, rax
        call    rdx

((DerivedClass2*)d)->DerivedCommonFunction2();

        cmp     QWORD PTR [rbp-24], 0
        je      .L14
        mov     rax, QWORD PTR [rbp-24]
        add     rax, 16
        jmp     .L15
.L14:
        mov     eax, 0
.L15:
        cmp     QWORD PTR [rbp-24], 0
        cmp     QWORD PTR [rbp-24], 0
        je      .L18
        mov     rdx, QWORD PTR [rbp-24]
        add     rdx, 16
        jmp     .L19
.L18:
        mov     edx, 0
.L19:
        mov     rdx, QWORD PTR [rdx]
        add     rdx, 8
        mov     rdx, QWORD PTR [rdx]
        mov     rdi, rax
        call    rdx

((Base*)d)->VirtualFunction();

        cmp     QWORD PTR [rbp-24], 0
        je      .L20
        mov     rax, QWORD PTR [rbp-24]
        mov     rax, QWORD PTR [rax]
        sub     rax, 24
        mov     rax, QWORD PTR [rax]
        mov     rdx, rax
        mov     rax, QWORD PTR [rbp-24]
        add     rax, rdx
        jmp     .L21
.L20:
        mov     eax, 0
.L21:
        cmp     QWORD PTR [rbp-24], 0
        cmp     QWORD PTR [rbp-24], 0
        je      .L24
        mov     rdx, QWORD PTR [rbp-24]
        mov     rdx, QWORD PTR [rdx]
        sub     rdx, 24
        mov     rdx, QWORD PTR [rdx]
        mov     rcx, rdx
        mov     rdx, QWORD PTR [rbp-24]
        add     rdx, rcx
        jmp     .L25
.L24:
        mov     edx, 0
.L25:
        mov     rdx, QWORD PTR [rdx]
        add     rdx, 8
        mov     rdx, QWORD PTR [rdx]
        mov     rdi, rax
        call    rdx
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.