「仮想基本クラス」について知りたい」とは何か、その意味。
例を示しましょう:
class Foo
{
public:
    void DoSomething() { /* ... */ }
};
class Bar : public virtual Foo
{
public:
    void DoSpecific() { /* ... */ }
};「仮想基本クラス」について知りたい」とは何か、その意味。
例を示しましょう:
class Foo
{
public:
    void DoSomething() { /* ... */ }
};
class Bar : public virtual Foo
{
public:
    void DoSpecific() { /* ... */ }
};回答:
仮想継承で使用される仮想基本クラスは、複数の継承を使用するときに、特定のクラスの複数の「インスタンス」が継承階層に表示されないようにする方法です。
次のシナリオを検討してください。
class A { public: void Foo() {} };
class B : public A {};
class C : public A {};
class D : public B, public C {};上記のクラス階層は、次のような「恐ろしいひし形」になります。
  A
 / \
B   C
 \ /
  DDのインスタンスは、Aを含むBと、Aを含むCで構成されます。したがって、Aの2つの「インスタンス」(より良い表現が必要)があります。
このシナリオがあると、あいまいになる可能性があります。これを行うとどうなりますか:
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の「インスタンス」が1つだけ階層に含まれていることを意味します。したがって
D d;
d.Foo(); // no longer ambiguousvirtualと、オブジェクトのレイアウトがひし形のように見えることです。使用しない場合virtual、オブジェクトのレイアウトは2つAのs を含むツリー構造のように見えます
                    補足として、Dreaded Diamondの問題は、基本クラスが複数回存在することです。したがって、通常の継承では、次のように考えています。
  A
 / \
B   C
 \ /
  Dしかし、メモリレイアウトでは、次のようになります。
A   A
|   |
B   C
 \ /
  DこれはD::foo()、を呼び出すときに曖昧さの問題がある理由を説明しています。ただし、実際の問題は、のメンバー変数を使用するときに発生しますA。たとえば、次のようなものだとします。
class A
{
    public :
       foo() ;
       int m_iValue ;
} ;あなたがアクセスしようとするでしょうときm_iValueからのD階層で、それは2表示されますので、コンパイラは、抗議しますm_iValue、ではないものを。そして、たとえばB::m_iValue(のA::m_iValue親であるB)をC::m_iValue変更しても、変更されません(つまり、のA::m_iValue親です)。C)。
ここで、仮想継承が便利になります。仮想継承は、1つのfoo()メソッドだけでなく1つだけの真のダイヤモンドレイアウトに戻りますm_iValue。
想像してみてください:
A いくつかの基本的な機能があります。B それにある種のクールなデータ配列を追加します(例)Cオブザーバーパターン(たとえば、m_iValue)のようないくつかのクールな機能を追加します。DBおよびから継承されるCため、から継承されAます。通常の継承では、m_iValueからの変更Dはあいまいであり、これを解決する必要があります。たとえあったとしても、中には2つm_iValuesありますDので、それを覚えて、2つを同時に更新することをお勧めします。
仮想継承では、m_iValuefromからの変更は問題Dありません...しかし...あなたが持っているとしましょうD。そのCインターフェースを介して、オブザーバーを接続しました。そして、そのBインターフェイスを介して、クールな配列を更新します。これには、直接変更するという副作用がありm_iValueます...
の変更はm_iValue(仮想アクセサメソッドを使用せずに)直接行わCれるため、リスニングを実装するコードがにありC、Bそれを認識していないため、「リスニング」スルーを介したオブザーバは呼び出されません...
階層にひし形がある場合は、その階層で何か問題があった可能性が95%あることを意味します。
仮想ベースを使用した多重継承について説明するには、C ++オブジェクトモデルの知識が必要です。また、トピックを明確に説明することは、コメントボックスではなく記事で行うのが最善です。
このテーマに関する私の疑問をすべて解決した、私が見つけた最良の読みやすい説明は、次の記事でした。 。http //www.phpcompiler.org/articles/virtualinheritance.html
それを読んだ後は、(コンパイラの作成者でない限り)トピックについて他に何も読む必要はありません...
仮想基本クラスはインスタンス化できないクラスです。そのクラスから直接オブジェクトを作成することはできません。
私はあなたが2つの非常に異なるものを混同していると思います。仮想継承は、抽象クラスと同じものではありません。仮想継承は、関数呼び出しの動作を変更します。場合によってはあいまいな関数呼び出しを解決することもあれば、非仮想継承で期待されるもの以外のクラスへの関数呼び出し処理を延期することもあります。
OJの親切な説明に追加したいと思います。
仮想継承には、代償が伴います。すべての仮想物と同様に、パフォーマンスに打撃を与えます。このパフォーマンスへの影響を回避する方法は、あまりエレガントではない可能性があります。
仮想的に派生してダイヤモンドを壊す代わりに、ダイヤモンドに別のレイヤーを追加して、次のようなものを得ることができます:
   B
  / \
D11 D12
 |   |
D21 D22
 \   /
  DDどのクラスも仮想的に継承せず、すべてパブリックに継承します。クラスD21とD22は、おそらく関数をプライベートとして宣言することによって、DDに対してあいまいな仮想関数f()を隠します。それらはそれぞれラッパー関数f1()とf2()を定義し、それぞれがクラスローカル(プライベート)f()を呼び出して、競合を解決します。クラスDDは、D11 :: f()が必要な場合はf1()を呼び出し、D12 :: f()が必要な場合はf2()を呼び出します。インラインでラッパーを定義すると、オーバーヘッドはほぼゼロになります。
もちろん、D11とD12を変更できる場合は、これらのクラス内で同じトリックを実行できますが、多くの場合そうではありません。
多重継承と仮想継承についてすでに述べられていることに加えて、ドブ博士のジャーナルに関する非常に興味深い記事があります:多重継承は有用と考えられます
あなたは少し混乱しています。いくつかの概念を混同しているかどうかはわかりません。
OPに仮想基本クラスがありません。あなただけの基本クラスがあります。
あなたは仮想継承を行いました。これは通常、複数の継承で使用されるため、複数の派生クラスは基本クラスのメンバーを複製せずに使用します。
純粋な仮想関数を持つ基本クラスはインスタンス化されません。これには、Paulが理解する構文が必要です。これは通常、派生クラスがこれらの関数を定義する必要があるために使用されます。
私はこれについてこれ以上説明したくありません。あなたが求めていることを完全には理解していないからです。
これは、仮想関数の呼び出しが「正しい」クラスに転送されることを意味します。
C ++ FAQ Lite FTW。
つまり、「ダイアモンド」階層が形成される複数継承シナリオでよく使用されます。次に、仮想継承は、そのクラスで関数を呼び出し、関数がその最下位クラスの上のクラスD1またはD2のいずれかに解決される必要がある場合、最下位クラスで作成されたあいまいさを解消します。FAQ項目を参照してください図と詳細をください。
強力な機能である姉妹委任でも使用されます(ただし、気弱な人には使用できません)。これを見て FAQをください。
また、Effective C ++第3版の項目40(第2版の43)も参照してください。
ダイヤモンド継承の実行可能な使用例
この例は、典型的なシナリオで仮想基本クラスを使用する方法を示しています:ダイヤモンドの継承を解決します。
#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);
}assert(A::aDefault == 0);メイン関数からのコンパイルエラー:aDefault is not a member of Agcc 5.4.0の使用。何をするつもりですか?
                    仮想クラスがありません仮想継承と同じで。インスタンス化できない仮想クラス、仮想継承はまったく別のものです。
ウィキペディアは、私よりもよく説明しています。http://en.wikipedia.org/wiki/Virtual_inheritance

典型的な3レベルの非ダイヤモンド、非仮想継承の継承では、新しい最も派生したオブジェクトをインスタンス化すると、newが呼び出され、オブジェクトに必要なサイズがコンパイラーによってクラス型から解決され、newに渡されます。
newには署名があります:
_GLIBCXX_WEAK_DEFINITION void *
operator new (std::size_t sz) _GLIBCXX_THROW (std::bad_alloc)そして、 malloc、voidポインタを返します
次に、これは最も派生したオブジェクトのコンストラクターに渡されます。コンストラクターはすぐに中央のコンストラクターを呼び出し、次に中央のコンストラクターがすぐに基本コンストラクターを呼び出します。次に、ベースは、オブジェクトの開始時に仮想テーブルへのポインタを格納し、その後にその属性を格納します。次に、これは中央のコンストラクターに戻ります。中央のコンストラクターは、仮想テーブルポインターを同じ場所に格納し、その後、基本コンストラクターによって格納されたであろう属性の後にその属性を格納します。これは、最も派生したコンストラクターに戻ります。このコンストラクターは、仮想テーブルへのポインターを同じ場所に格納し、その後、中間のコンストラクターによって格納されたであろう属性の後にその属性を格納します。
仮想テーブルポインタが上書きされるため、仮想テーブルポインタは常に最も派生したクラスの1つになります。仮想性は最も派生したクラスに向かって伝播するため、関数が中間クラスで仮想である場合、関数は最も派生したクラスでは仮想になりますが、基本クラスでは仮想になります。最も派生したクラスのインスタンスを基本クラスへのポインターに多態的にキャストすると、コンパイラーはこれを仮想テーブルへの間接呼び出しに解決せず、代わりに関数を直接呼び出します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+16virtualそれが伝播するために、それがより少ない派生クラスで仮想である場合、より多くの派生クラスで再び必要とされません。ただし、この関数を使用して、関数が実際に仮想関数であることを示すことができます。継承するクラスの型定義を確認する必要はありません。
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 dDerivedDerivedClass::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
        retBase::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
        retDerivedDerivedClass::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
        retVTT 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()その後、オブジェクト+ 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    rdxd->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    rdxd->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    raxd->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