C ++ 'da sanal temel sınıf nedir?


403

" Sanal temel sınıf " ın ne olduğunu ve ne anlama geldiğini bilmek istiyorum .

Bir örnek göstereyim:

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

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

'çoklu miras' sanal taban sınıfları kullanmalıyız çünkü eğer A sınıfı üye değişkeni int a'ya ve B sınıfı da üye int a'ya ve C sınıfı sınıfı A ve B'yi miras alırsa, hangi 'a'nın kullanılacağına nasıl karar verebiliriz?
Namit Sinha

2
@NamitSinha hayır, sanal miras yok değil bu sorunu çözmek. A üyesi yine de belirsiz olurdu
Ichthyo

@NamitSinha Sanal kalıtım birden fazla kalıtımla ilgili belirsizliği ortadan kaldırmak için sihirli bir araç değildir. Dolaylı bir tabana birden fazla kez sahip olma sorununu "çözer". Bu sadece paylaşılması amaçlanmışsa bir sorundur (çoğu zaman ama her zaman böyle değil).
curiousguy

Yanıtlar:


533

Sanal kalıtımda kullanılan sanal temel sınıflar, belirli bir sınıfın çoklu kalıtım hiyerarşisinde görünen birden çok "örneğini" önlemenin bir yoludur.

Aşağıdaki senaryoyu düşünün:

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

Yukarıdaki sınıf hiyerarşisi şuna benzer "korkunç elmas" ile sonuçlanır:

  A
 / \
B   C
 \ /
  D

D örneği A'yı içeren B'yi ve A'yı da içeren C'yi içerecektir. Böylece A'nın iki "örneğini" (daha iyi ifade etmek için) var.

Bu senaryoya sahip olduğunuzda, belirsizlik olasılığınız olur. Bunu yaptığınızda ne olur:

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

Bu sorunu çözmek için sanal kalıtım var. Sınıflarınızı devralırken sanal belirttiğinizde, derleyiciye yalnızca tek bir örnek istediğinizi söylersiniz.

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

Bu, hiyerarşide A'nın yalnızca bir "örneği" olduğu anlamına gelir. bundan dolayı

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

Bu küçük bir özet. Daha fazla bilgi için bunu ve bunu okuyun . Burada da iyi bir örnek var .


7
@ Bohdan hayır değil :)
OJ.

6
@OJ. neden olmasın? Onlar komik :)
Bohdan

15
@Bohdan sanal anahtar kelimeyi daha az kullanır, çünkü sanal anahtar kelimeyi kullandığımızda ağır ağırlık mekanizması uygulanır. Böylece program verimliliğiniz azalacaktır.
Sagar

73
"Korkunç elmas" diyagramınız kafa karıştırıcı olsa da, yaygın olarak kullanılıyor gibi görünüyor. Bu aslında bir nesne düzeni değil , sınıf mirası ilişkilerini gösteren bir diyagramdır . Kafa karıştırıcı kısım, eğer kullanırsak virtual, nesne düzeninin elmas gibi görünmesidir; ve eğer kullanmazsak virtualnesne düzeni iki As içeren bir ağaç yapısına benziyor
MM

5
Bu cevabı MM tarafından belirtilen nedenden ötürü küçümsemeliyim - diyagram yazının tersini ifade ediyor.
David Stone

251

Bellek düzeni hakkında

Bir yan not olarak, Dreaded Diamond ile ilgili sorun, temel sınıfın birçok kez mevcut olmasıdır. Yani düzenli miras ile, sahip olduğunuza inanıyorsunuz:

  A
 / \
B   C
 \ /
  D

Ancak bellek düzeninde:

A   A
|   |
B   C
 \ /
  D

Bu, çağrı yaparken neden D::foo()bir belirsizlik sorununuz olduğunu açıklar . Ancak asıl sorun, üye değişkenini kullanmak istediğinizde ortaya çıkar A. Örneğin, diyelim ki:

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

Erişmeye çalışacağım zaman m_iValuegelen Dhiyerarşisinde, bu iki göreceksiniz, çünkü derleyici, protesto edecek m_iValue, bir değil. Ve bir tanesini değiştirirseniz, diyelim ki, B::m_iValue(yani A::m_iValueebeveyni B), C::m_iValuedeğiştirilmeyecektir ( A::m_iValueebeveyni C).

Sanal kalıtımın işe yaradığı yer, burada olduğu gibi, sadece tek bir foo()yöntemle değil , aynı zamanda bir ve sadece biriyle gerçek bir elmas düzenine geri döneceksiniz m_iValue.

Ne ters gidebilir ki?

Hayal etmek:

  • A bazı temel özelliklere sahiptir.
  • B buna bir çeşit harika veri dizisi ekler (örneğin)
  • Cbir gözlemci deseni gibi harika bir özellik ekler (örneğin, açık m_iValue).
  • Ddevralır Bve Cve böylece gelen A.

Normal miras ile değiştirerek m_iValuegelen Dbelirsiz ve bu çözülmelidir. Olsa bile, içeride iki tane m_iValuesvar D, bu yüzden bunu hatırlamanız ve aynı anda ikisini güncellemeniz daha iyi olur.

Sanal miras sayesinde, modifiye m_iValuedan Dsahip olduğun ... Hadi diyelim tamam ... Ama D. CArayüzü sayesinde bir gözlemci iliştirdiniz. Ve Barayüzü sayesinde, doğrudan değiştirmenin yan etkisi olan serin diziyi güncellersiniz m_iValue...

Değişikliği m_iValuedoğrudan yapıldığından (sanal bir erişimci yöntemi kullanmadan), "dinleyen" gözlemci Cçağrılmaz, çünkü dinlemeyi uygulayan kod içeride Cve Bbunu bilmez ...

Sonuç

Hiyerarşinizde bir elmas varsa, bu hiyerarşide yanlış bir şey yapma olasılığınızın% 95 olduğu anlamına gelir.


'Neyin yanlış gidebileceği', birden fazla miras nedeniyle değil, bir temel üyeye doğrudan erişimdir. 'B' kurtulmak ve aynı sorun var.Genel kural: 'onun özel değilse, sanal olmalı' sorunu önler. M_iValue sanal değil ve bu nedenle özel olmalı
Chris Dodd

4
@Chris Dodd: Tam olarak değil. M_iValue ile ne olursa olsun herhangi bir sembole ( ör. Typedef , üye değişkeni, üye işlevi, temel sınıfa döküm vb. ) Bu gerçekten çoklu bir kalıtım sorunu, kullanıcıların Java yoluna gidip "Çoklu kalıtım% 100 kötülük, bunu arayüzler ile yapalım" yerine, çoklu kalıtımın doğru şekilde kullanılması gerektiğini bilmeleri gereken bir konudur.
Paercebal

Merhaba, Sanal anahtar kelime kullandığımızda, A'nın yalnızca bir kopyası olacak. Sorum şu: B veya C'den gelip gelmediğini nasıl bilebiliriz? Sorum hiç geçerli mi?
user875036

@ user875036: A hem B hem de C'den geliyor. Aslında, sanallık birkaç şeyi değiştirir (örneğin D, A'yı yapıcı olarak adlandırır, B'yi veya C'yi çağırmaz). Hem B hem de C (ve D) A'ya bir işaretçi içerir
paercebal

3
FWIW, birisinin merak etmesi durumunda üye değişkenler sanal olamaz - sanal fonksiyonlar için bir tanımlayıcıdır . SO referansı: stackoverflow.com/questions/3698831/…
rholmes

34

Çoklu kalıtımın sanal tabanlarla açıklanması, C ++ nesne modeli hakkında bilgi gerektirir. Ve konuyu açık bir şekilde açıklamak en iyi yorum kutusunda değil, bir makalede yapılır.

Bu konudaki tüm şüphelerimi çözdüğünü bulduğum en iyi, okunabilir açıklama bu makaleydi: http://www.phpcompiler.org/articles/virtualinheritance.html

Bunu okuduktan sonra (derleyici yazar değilseniz) konuyla ilgili başka bir şey okumanız gerekmeyecek ...


10

Sanal temel sınıf, somutlaştırılamayan bir sınıftır: buradan doğrudan nesne oluşturamazsınız.

Bence çok farklı iki şeyi karıştırıyorsun. Sanal kalıtım soyut bir sınıfla aynı şey değildir. Sanal kalıtım işlev çağrılarının davranışını değiştirir; bazen aksi takdirde belirsiz olan fonksiyon çağrılarını çözer, bazen sanal olmayan bir mirasta beklediğinden farklı bir sınıfa fonksiyon çağrısı işlemeyi bozar.


7

OJ'nin nazik açıklamalarına eklemek istiyorum.

Sanal kalıtım bedelsiz gelmez. Sanal her şeyde olduğu gibi, bir performans vuruşu elde edersiniz. Bu performans vuruşunun etrafında muhtemelen daha az zarif olan bir yol var.

Neredeyse türeterek pırlantayı kırmak yerine, pırlantaya başka bir katman ekleyebilir, şöyle bir şey elde edebilirsiniz:

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

Sınıfların hiçbiri sanal olarak miras almıyor, hepsi de alenen miras alıyor. D21 ve D22 sınıfları daha sonra DD için belirsiz olan f () sanal fonksiyonunu, belki de özel fonksiyonunu ilan ederek gizleyecektir. Her biri sırasıyla bir sarmalayıcı işlevi tanımlarlar, sırasıyla f1 () ve f2 (), her biri sınıf-yerel (özel) f () olarak adlandırılır, böylece çatışmalar çözülür. DD sınıfı, D11 :: f () istiyorsa f1 () ve D12 :: f () istiyorsa f2 () öğesini çağırır. Sargıları satır içinde tanımlarsanız, muhtemelen sıfır ek yük alırsınız.

Tabii ki, D11 ve D12'yi değiştirebilirseniz, bu sınıfların içinde aynı hileyi yapabilirsiniz, ancak çoğu zaman böyle değildir.


2
Bu, az çok zarif veya belirsizlikleri çözme meselesi değildir (bunun için her zaman açık xxx :: spesifikasyonlarını kullanabilirsiniz). Sanal olmayan kalıtımla, DD sınıfının her örneğinin iki bağımsız B örneği vardır. Sınıf statik olmayan tek bir veri üyesine sahip olur olmaz, sanal ve sanal olmayan kalıtım sadece sözdiziminden daha fazla farklılık gösterir.
user3489112

@ user3489112 En kısa sürede ... hiçbir şey. Sanal ve sanal olmayan kalıtım semantik olarak farklılık gösterir.
curiousguy


1

Biraz kafa karıştırıcısın. Bazı kavramları karıştırıp karıştırmadığınızı bilmiyorum.

OP'nizde sanal bir temel sınıfınız yok. Sadece bir temel sınıfınız var.

Sanal miras yaptın. Bu genellikle çoklu kalıtımda kullanılır, böylece çoklu türetilmiş sınıflar, temel sınıfın üyelerini çoğaltmadan kullanır.

Saf sanal işlevi olan bir temel sınıf somutlaştırılmaz. bu Paul'ün sözdizimini gerektirir. Genellikle türetilmiş sınıfların bu işlevleri tanımlaması gerekir.

Bu konuda daha fazla açıklamak istemiyorum çünkü sorduğunuz şeyi tam olarak anlayamıyorum.


1
Sanal bir mirasta kullanılan bir "temel sınıf" bir "sanal temel sınıf" haline gelir (bu kesin miras bağlamında).
Luc Hermitte

1

Bu, sanal bir işleve yapılan çağrının "sağ" sınıfa yönlendirileceği anlamına gelir.

C ++ SSS Lite FTW.

Kısacası, genellikle bir "elmas" hiyerarşisinin oluştuğu çoklu kalıtım senaryolarında kullanılır. Sanal devralma, daha sonra, o sınıftaki işlevi çağırdığınızda ve işlevin o alt sınıfın üzerindeki D1 veya D2 sınıfına çözümlenmesi gerektiğinde, alt sınıfta oluşturulan belirsizliği bozar. Bkz SSS öğeyi bir diyagram ve detaylar için.

Aynı zamanda güçlü bir özellik olan kardeş delegasyonunda da kullanılır (ancak kalp zayıflığı için değil). Bu SSS'ye bakın .

Ayrıca, Etkili C ++ 3. baskıdaki Madde 40'a (2. baskıdaki 43) bakın.


1

Elmas miras çalıştırılabilir kullanım örneği

Bu örnek, tipik bir senaryoda sanal bir taban sınıfının nasıl kullanılacağını gösterir: elmas mirasını çözmek için.

#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);ana işlev bana derleme hatası veriyor: aDefault is not a member of Agcc 5.4.0 kullanarak. Ne yapmalı?
SebNag

@SebTu ah teşekkürler, kopya yapıştırmadan kaldırmayı unuttuğum bir şey, şimdi kaldırıldı. Örnek hala onsuz anlamlı olmalıdır.
Ciro Santilli 法轮功 冠状 :00 六四 事件 法轮功

0

Sanal sınıflar, sanal kalıtımla aynı şey değildir . Anlayamadığınız sanal sınıflar, sanal kalıtım tamamen başka bir şeydir.

Wikipedia bunu benden daha iyi anlatıyor. http://en.wikipedia.org/wiki/Virtual_inheritance


6
C ++ 'da "sanal sınıflar" diye bir şey yoktur. Bununla birlikte, belirli bir miras ile ilgili "sanal" olan "sanal temel sınıflar" vardır. Ne demek istediğiniz resmi olarak "soyut sınıflar" olarak adlandırılır.
Luc Hermitte

@LucHermitte, C ++ 'da kesinlikle sanal sınıflar vardır. Bunu kontrol edin: en.wikipedia.org/wiki/Virtual_class .
Rafid

"hata: 'sanal' yalnızca işlevler için belirtilebilir". Bunun hangi dili olduğunu bilmiyorum. Ancak C ++ ' da sanal sınıf diye bir şey yoktur .
Luc Hermitte

0

Düzenli Kalıtım

Tipik 3 seviyeli elmas olmayan sanal olmayan kalıtım mirasıyla, en çok türetilmiş yeni bir nesneyi başlattığınızda, yeni çağrılır ve nesne için gereken boyut derleyici tarafından sınıf türünden çözülür ve yeniye geçirilir.

yeni bir imzası var:

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

Ve mallocboş işaretçiyi döndürerek bir çağrı yapar

Bu daha sonra hemen orta yapıcıyı çağıracak ve daha sonra orta yapıcı hemen taban yapıcısını çağıracak olan en türetilmiş nesnenin yapıcısına geçirilir. Taban daha sonra nesnenin başlangıcında sanal tablosuna bir işaretçi ve sonrasında da niteliklerini depolar. Bu, daha sonra sanal tablo işaretçisini aynı konumda depolayacak olan orta kurucuya ve daha sonra temel kurucu tarafından saklanacak niteliklerden sonra niteliklerine geri döner. İşaretçiyi sanal tablosuna aynı konumda ve daha sonra da orta kurucu tarafından saklanacak niteliklerden sonraki niteliklerini depolayan en türetilmiş kurucuya döner.

Sanal tablo işaretçisinin üzerine yazıldığından, sanal tablo işaretçisi her zaman en türetilmiş sınıftan biri olur. Sanallık en türetilmiş sınıfa doğru ilerler, bu nedenle bir işlev orta sınıfta sanal ise, en çok türetilmiş sınıfta sanal olur, ancak temel sınıfta olmaz. En türetilmiş sınıfın bir örneğini temel sınıfa bir işaretçiye polimorfik olarak uygularsanız, derleyici bunu sanal tabloya yapılan dolaylı çağrıya çözümlemez ve bunun yerine işlevi doğrudan çağırır A::function(). Bir işlev, onu yayınladığınız tür için sanal ise, o zaman sanal tabloya her zaman en türetilmiş sınıfın işlevi olacak bir çağrıyı çözer. Bu tür için sanal değilse Type::function(), nesne işaretçisini çağırır ve ona iletir, Tür'e yayınlar.

Aslında sanal tablosuna işaretçi dediğimde, sanal tabloya her zaman 16'lık bir ofset.

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

virtualdaha az türetilmiş bir sınıfta sanal olması nedeniyle daha türetilmiş sınıflarda yeniden gerekli değildir. Ancak, fonksiyonun gerçekten de sanal bir fonksiyon olduğunu göstermek için kullanılabilir, sınıflarının tip tanımlarını miras almak zorunda kalmadan.

override bu işlevin bir şeyi geçersiz kıldığını söyleyen başka bir derleyici koruyucusudur ve eğer değilse bir derleyici hatası atar.

= 0 bunun soyut bir işlev olduğu anlamına gelir

final sanal işlevin daha türetilmiş bir sınıfta yeniden uygulanmasını engeller ve en türetilmiş sınıfın sanal tablosunun bu sınıfın son işlevini içerdiğinden emin olur.

= default dokümantasyonda derleyicinin varsayılan uygulamayı kullanacağını açıkça belirtir

= delete buna bir çağrı yapılmaya çalışılırsa derleyici hatası ver

Sanal Kalıtım

Düşünmek

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();
 }

Bas sınıfını neredeyse miras almadan, şöyle görünen bir nesne elde edersiniz:

Bunun yerine:

Yani 2 temel nesne olacak.

Yeni çağrıldıktan sonra yukarıda sanal elmas miras durumda, bu en türetilmiş yapıcı çağrıları ve bu yapıcı içinde, bunun yerine sadece çağıran çağıran, onun sanal masa tabloya uzaklıklar geçen bütün 3 türetilen Kurucular çağırır DerivedClass1::DerivedClass1()ve DerivedClass2::DerivedClass2()sonra bu hem arama veBase::Base()

Aşağıdakilerin tümü -O0 hata ayıklama modunda derlenir, bu nedenle yedekli montaj yapılır

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()Nesne ofsetine (32) bir işaretçi ile çağrı yapar. Base, aldığı adrese ve sonrasındaki üyelerine sanal tablosuna bir işaretçi depolar.

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()daha sonra DerivedClass1::DerivedClass1()nesne ofseti 0'a bir işaretçi ile çağrı yapar ve ayrıcaVTT 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()Daha sonra nesne + 16 adresini ve için VTT adresini geçer DerivedDerivedClass+24için DerivedClass2::DerivedClass2()montaj aynıdır olan DerivedClass1::DerivedClass1()hattı için hariç mov DWORD PTR [rax+8], 3belli bir yerine 4 3 sahiptir d = 4.

Bundan sonra, nesnedeki 3 sanal tablo işaretçisinin tümünü DerivedDerivedClass, bu sınıfın gösterimine göre ofsetlere işaretçilerle değiştirir .

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.