Chcę wiedzieć, czym jest „ wirtualna klasa bazowa ” i co ona oznacza.
Pokażę przykład:
class Foo
{
public:
void DoSomething() { /* ... */ }
};
class Bar : public virtual Foo
{
public:
void DoSpecific() { /* ... */ }
};
Chcę wiedzieć, czym jest „ wirtualna klasa bazowa ” i co ona oznacza.
Pokażę przykład:
class Foo
{
public:
void DoSomething() { /* ... */ }
};
class Bar : public virtual Foo
{
public:
void DoSpecific() { /* ... */ }
};
Odpowiedzi:
Wirtualne klasy podstawowe, używane w wirtualnym dziedziczeniu, są sposobem na zapobieganie pojawianiu się wielu „wystąpień” danej klasy w hierarchii dziedziczenia podczas korzystania z wielokrotnego dziedziczenia.
Rozważ następujący scenariusz:
class A { public: void Foo() {} };
class B : public A {};
class C : public A {};
class D : public B, public C {};
Powyższa hierarchia klas skutkuje „przerażającym diamentem”, który wygląda następująco:
A
/ \
B C
\ /
D
Instancja D będzie złożona z B, która obejmuje A, i C, która również obejmuje A. Zatem masz dwa „wystąpienia” (z braku lepszego wyrażenia) A.
Kiedy masz taki scenariusz, masz możliwość niejasności. Co się stanie, gdy to zrobisz:
D d;
d.Foo(); // is this B's Foo() or C's Foo() ??
Wirtualne dziedziczenie służy rozwiązaniu tego problemu. Kiedy określasz wirtualny podczas dziedziczenia klas, mówisz kompilatorowi, że chcesz tylko jednej instancji.
class A { public: void Foo() {} };
class B : public virtual A {};
class C : public virtual A {};
class D : public B, public C {};
Oznacza to, że istnieje tylko jedna „instancja” A zawarta w hierarchii. W związku z tym
D d;
d.Foo(); // no longer ambiguous
To jest mini podsumowanie. Aby uzyskać więcej informacji, przeczytaj to i to . Dobry przykład jest również dostępny tutaj .
virtual
, układ obiektu wygląda jak diament; a jeśli nie używamy, virtual
układ obiektu wygląda jak struktura drzewa, która zawiera dwa A
s
Na marginesie, problem z Przerażonym Diamentem polega na tym, że klasa podstawowa występuje wielokrotnie. Więc przy regularnym dziedziczeniu uważasz, że masz:
A
/ \
B C
\ /
D
Ale w układzie pamięci masz:
A A
| |
B C
\ /
D
To wyjaśnia, dlaczego podczas połączenia D::foo()
masz problem z dwuznacznością. Ale prawdziwy problem pojawia się, gdy chcesz użyć zmiennej członka A
. Załóżmy na przykład, że mamy:
class A
{
public :
foo() ;
int m_iValue ;
} ;
Gdy spróbujesz uzyskać dostęp m_iValue
z D
, kompilator zaprotestuje, ponieważ w hierarchii zobaczy dwa m_iValue
, a nie jeden. A jeśli zmodyfikujesz jeden, powiedzmy B::m_iValue
(to jest A::m_iValue
nadrzędny B
), C::m_iValue
nie zostanie zmodyfikowany (to jest A::m_iValue
nadrzędny C
).
Tutaj przydaje się wirtualne dziedziczenie, ponieważ dzięki niemu wrócisz do prawdziwego układu diamentów, używając nie tylko jednej foo()
metody, ale także jednej i tylko jednej m_iValue
.
Wyobrażać sobie:
A
ma podstawową funkcję.B
dodaje do tego jakiś fajny zestaw danych (na przykład)C
dodaje do tego jakąś fajną funkcję, taką jak wzorzec obserwatora (na przykład włączony m_iValue
).D
dziedziczy po B
i C
, a zatem i od A
.W przypadku normalnego dziedziczenia zmiana m_iValue
z D
jest niejednoznaczna i należy to rozwiązać. Nawet jeśli tak, są dwa w m_iValues
środku D
, więc lepiej o tym pamiętaj i zaktualizuj oba jednocześnie.
W przypadku wirtualnego dziedziczenia zmiana m_iValue
z D
jest w porządku ... Ale ... Powiedzmy, że masz D
. Poprzez jego C
interfejs dołączyłeś obserwatora. Za pomocą tego B
interfejsu aktualizujesz fajną tablicę, której efektem ubocznym jest bezpośrednia zmiana m_iValue
...
Ponieważ zmiana m_iValue
odbywa się bezpośrednio (bez użycia metody wirtualnego akcesorium), obserwator „nasłuchujący” C
nie zostanie wywołany, ponieważ kod implementujący nasłuchiwanie jest włączony C
i B
nie wie o tym ...
Jeśli masz diament w swojej hierarchii, oznacza to, że masz 95% prawdopodobieństwa, że zrobiłeś coś złego z tą hierarchią.
Wyjaśnienie wielokrotnego dziedziczenia za pomocą wirtualnych baz wymaga znajomości modelu obiektowego C ++. Wyjaśnienie tego tematu najlepiej jest zrobić w artykule, a nie w polu komentarza.
Najlepszym, czytelnym wyjaśnieniem, które rozwiązało wszystkie moje wątpliwości w tym temacie, był ten artykuł: http://www.phpcompiler.org/articles/virtualinheritance.html
Naprawdę nie będziesz musiał czytać niczego więcej na ten temat (chyba że jesteś pisarzem kompilatora) po przeczytaniu tego ...
Wirtualna klasa podstawowa to klasa, której nie można utworzyć: nie można na jej podstawie tworzyć bezpośrednich obiektów.
Myślę, że mylisz dwie bardzo różne rzeczy. Dziedziczenie wirtualne nie jest tym samym, co klasa abstrakcyjna. Dziedziczenie wirtualne modyfikuje zachowanie wywołań funkcji; czasami rozwiązuje wywołania funkcji, które w innym przypadku byłyby niejednoznaczne, czasami odkłada obsługę wywołań funkcji na klasę inną niż ta, której można oczekiwać w dziedziczeniu innym niż wirtualny.
Chciałbym dodać do uprzejmych wyjaśnień Dz.U.
Dziedziczenie wirtualne nie przychodzi bez ceny. Podobnie jak w przypadku wszystkich rzeczy wirtualnych, masz hit wydajności. Istnieje sposób obejścia tego hitu wydajności, który może być mniej elegancki.
Zamiast łamać diament, czerpiąc wirtualnie, możesz dodać kolejną warstwę do diamentu, aby uzyskać coś takiego:
B
/ \
D11 D12
| |
D21 D22
\ /
DD
Żadna z klas nie dziedziczy wirtualnie, wszystkie dziedziczą publicznie. Klasy D21 i D22 będą wtedy ukrywać funkcję wirtualną f (), która jest niejednoznaczna dla DD, być może przez uznanie tej funkcji za prywatną. Każdy z nich zdefiniowałby odpowiednio funkcję opakowania, odpowiednio f1 () i f2 (), każde wywołujące klasę lokalną (prywatną) f (), rozwiązując w ten sposób konflikty. Klasa DD wywołuje f1 (), jeśli chce D11 :: f () i f2 (), jeśli chce D12 :: f (). Jeśli zdefiniujesz zawijanie w linii, prawdopodobnie uzyskasz około zera narzutu.
Oczywiście, jeśli możesz zmienić D11 i D12, możesz wykonać tę samą sztuczkę w tych klasach, ale często tak nie jest.
Oprócz tego, co już powiedziano na temat wielokrotnego i wirtualnego dziedziczenia, istnieje bardzo interesujący artykuł na temat Dr Dobb's Journal: Wielokrotne dziedziczenie uważane za przydatne
Jesteś trochę zagmatwany. Nie wiem, czy mieszacie jakieś koncepcje.
Nie masz wirtualnej klasy bazowej w swoim OP. Masz tylko klasę podstawową.
Zrobiłeś wirtualne dziedzictwo. Jest to zwykle używane w przypadku wielokrotnego dziedziczenia, dzięki czemu wiele klas pochodnych korzysta z elementów klasy podstawowej bez ich odtwarzania.
Klasa podstawowa z czystą funkcją wirtualną nie jest tworzona. wymaga to składni, którą zna Paul. Jest zwykle używany, aby klasy pochodne musiały zdefiniować te funkcje.
Nie chcę więcej o tym wyjaśniać, ponieważ nie rozumiem, o co pytasz.
Oznacza to, że wywołanie funkcji wirtualnej zostanie przekierowane do „właściwej” klasy.
C ++ FAQ Lite FTW.
Krótko mówiąc, często stosuje się go w scenariuszach wielokrotnego dziedziczenia, w których powstaje hierarchia „diamentowa”. Dziedziczenie wirtualne przełamie dwuznaczność powstałą w dolnej klasie, gdy wywołasz funkcję w tej klasie, a funkcja musi zostać rozstrzygnięta na klasę D1 lub D2 powyżej tej dolnej klasy. Diagram i szczegóły znajdują się w części FAQ .
Jest również stosowany w delegacji siostrzanej , co jest potężną funkcją (choć nie dla osób o słabym sercu). Zobacz to FAQ.
Zobacz także punkt 40 w Efektywnym trzecim wydaniu C ++ (43 w drugim wydaniu).
Przykład użycia dziedziczonego diamentu
Ten przykład pokazuje, jak używać wirtualnej klasy bazowej w typowym scenariuszu: w celu rozwiązania dziedziczenia diamentów.
#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);
z głównej funkcji daje mi błąd kompilacji: aDefault is not a member of A
użycie gcc 5.4.0. Co to ma robić?
Klasy wirtualne to nie to samo, co wirtualne dziedziczenie. Wirtualne klasy, których nie można utworzyć, wirtualne dziedziczenie to coś zupełnie innego.
Wikipedia opisuje to lepiej niż potrafię. http://en.wikipedia.org/wiki/Virtual_inheritance
W przypadku typowego 3-poziomowego nie-diamentowego dziedziczenia niebędącego wirtualnym dziedziczeniem podczas tworzenia nowego najbardziej pochodnego obiektu wywoływane jest nowe, a rozmiar wymagany dla obiektu jest określany przez typ kompilatora i przekazywany do nowego.
nowy ma podpis:
_GLIBCXX_WEAK_DEFINITION void *
operator new (std::size_t sz) _GLIBCXX_THROW (std::bad_alloc)
I wykonuje połączenie malloc
, zwracając wskaźnik pustki
Jest on następnie przekazywany do konstruktora najbardziej pochodnego obiektu, który natychmiast wywoła środkowy konstruktor, a następnie środkowy konstruktor natychmiast wywoła konstruktor podstawowy. Baza następnie przechowuje wskaźnik do wirtualnej tabeli na początku obiektu, a następnie za nim atrybutów. Następnie wraca do środkowego konstruktora, który zapisze swój wirtualny wskaźnik tabeli w tej samej lokalizacji, a następnie jego atrybuty po atrybutach, które byłyby przechowywane przez konstruktora podstawowego. Powraca do najbardziej pochodnego konstruktora, który przechowuje wskaźnik do swojej wirtualnej tabeli w tym samym miejscu, a następnie jego atrybuty po atrybutach, które byłyby przechowywane przez środkowy konstruktor.
Ponieważ wskaźnik wirtualnej tabeli jest nadpisywany, wskaźnik wirtualnej tabeli zawsze jest jedną z najbardziej pochodnych klas. Wirtualność rozprzestrzenia się w kierunku klasy najbardziej pochodnej, więc jeśli funkcja jest wirtualna w klasie średniej, będzie wirtualna w klasie najbardziej pochodnej, ale nie w klasie bazowej. Jeśli odrzucisz polimorficznie instancję klasy najbardziej pochodnej do wskaźnika do klasy podstawowej, kompilator nie rozwiąże tego w wywołaniu pośrednim do tabeli wirtualnej, a zamiast tego wywoła funkcję bezpośrednio A::function()
. Jeśli funkcja jest wirtualna dla typu, do którego ją rzuciłeś, to rozwiąże wywołanie do wirtualnej tabeli, która zawsze będzie klasą najbardziej pochodną. Jeśli nie jest wirtualny dla tego typu, po prostu wywoła Type::function()
i przekaże do niego wskaźnik obiektu, rzutując na Typ.
W rzeczywistości, gdy mówię wskaźnik do wirtualnej tabeli, w rzeczywistości zawsze jest to przesunięcie o 16 względem wirtualnej tabeli.
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
nie jest ponownie wymagany w klasach pochodnych, jeśli jest wirtualny w klasie mniej pochodnej, ponieważ się propaguje. Ale można go użyć do pokazania, że funkcja jest rzeczywiście funkcją wirtualną, bez konieczności sprawdzania klas, które dziedziczy definicje typów.
override
jest kolejną ochroną kompilatora, która mówi, że ta funkcja coś przesłania, a jeśli nie, to zgłasza błąd kompilatora.
= 0
oznacza, że jest to funkcja abstrakcyjna
final
zapobiega ponownemu wdrożeniu funkcji wirtualnej w klasie bardziej pochodnej i upewni się, że wirtualna tabela najbardziej pochodnej klasy zawiera ostateczną funkcję tej klasy.
= default
wyjaśnia w dokumentacji, że kompilator użyje domyślnej implementacji
= delete
podać błąd kompilatora, jeśli nastąpi próba wywołania tego
Rozważać
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();
}
Bez dziedziczenia klasy basowej otrzymasz obiekt, który wygląda następująco:
Zamiast tego:
Tj. Będą 2 obiekty podstawowe.
W wirtualnym diamentowej spadków powyżej sytuacji, gdy nowy nazywa, wywołuje konstruktor większości pochodzący iw tym konstruktora, wywołuje wszystkie 3 konstruktorów pochodzące przechodzących przesunięć w swoim wirtualnym stół, zamiast dzwonić po prostu dzwoni DerivedClass1::DerivedClass1()
i DerivedClass2::DerivedClass2()
czym te oba wywołującyBase::Base()
Wszystko zostało skompilowane w trybie debugowania -O0, więc będzie zbędny zestaw
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
Wywołuje Base::Base()
wskaźnik za pomocą przesunięcia obiektu 32. Baza przechowuje wskaźnik do wirtualnej tabeli pod adresem, który otrzymuje, a członkowie po nim.
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()
następnie wywołuje DerivedClass1::DerivedClass1()
za pomocą wskaźnika do obiektu offset 0, a także przekazuje adresVTT 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()
następnie przekazuje adres obiektu + 16 i adres dla VTT DerivedDerivedClass+24
do DerivedClass2::DerivedClass2()
których montaż jest identyczna DerivedClass1::DerivedClass1()
z wyjątkiem linii mov DWORD PTR [rax+8], 3
, która ma oczywiście 4 zamiast 3 na d = 4
.
Następnie zastępuje wszystkie 3 wirtualne wskaźniki tabeli w obiekcie wskaźnikami przesunięć w tabeli DerivedDerivedClass
vtable do reprezentacji dla tej klasy.
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