" 가상 기본 클래스 "가 무엇이며 그 의미가 무엇인지 알고 싶습니다 .
예를 보여 드리겠습니다.
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
\ /
D
D의 인스턴스는 A를 포함하는 B와 A를 포함하는 C로 구성됩니다. 따라서 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
이것은 간단한 요약입니다. 자세한 내용은 this 와 this를 읽으십시오 . 여기 에서도 좋은 예가 있습니다 .
virtual
하면 객체 레이아웃이 다이아몬드처럼 보입니다. 우리가 사용하지 않으면 virtual
객체 레이아웃은 두 개의 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
하면 계층 구조에서 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
B
및 C
에서 상속받습니다 A
.일반 상속으로 수정 m_iValue
에서 것은 D
모호하며이 해결해야합니다. 있어도 m_iValues
안에 2 개가 있습니다D
있으므로이를 기억하고 동시에 두 개를 업데이트하는 것이 좋습니다.
가상 상속으로 수정 m_iValue
에서 것은 D
당신이 가지고 ...하자 말 괜찮습니다 ...하지만 D
. C
인터페이스를 통해 관찰자를 첨부했습니다. B
인터페이스 를 통해 멋진 배열을 업데이트하면 직접 변경하는 부작용이 있습니다 m_iValue
...
변경은 m_iValue
가상 액세서 메소드를 사용하지 않고 직접 수행되므로 청취를 C
구현하는 코드가에 C
있고 B
알 수 없으므로 "듣기"를 통한 관찰자 는 호출 되지 않습니다.
계층 구조에 다이아몬드가 있으면 해당 계층 구조에 문제가있을 가능성이 95 %라는 의미입니다.
가상 기반으로 다중 상속을 설명하려면 C ++ 객체 모델에 대한 지식이 필요합니다. 주제를 명확하게 설명하는 것은 주석 상자가 아닌 기사에서 가장 잘 수행됩니다.
이 주제에 대한 나의 모든 의구심을 해결 한 가장 읽기 쉬운 설명은 다음 기사입니다. http://www.phpcompiler.org/articles/virtualinheritance.html
그것을 읽은 후에는 주제에 대한 다른 것을 읽을 필요가 없습니다 (컴파일러 라이터가 아닌 한).
OJ의 친절 설명에 추가하고 싶습니다.
가상 상속은 가격이 없으면 오지 않습니다. 가상의 모든 것과 마찬가지로 성능이 저하됩니다. 이 성능 적중에는 덜 우아 할 수있는 방법이 있습니다.
가상으로 파생하여 다이아몬드를 깨는 대신 다이아몬드에 다른 레이어를 추가하여 다음과 같은 것을 얻을 수 있습니다.
B
/ \
D11 D12
| |
D21 D22
\ /
DD
어떤 클래스도 가상으로 상속되지 않으며 모두 공개적으로 상속됩니다. 클래스 D21과 D22는 아마도 private 함수를 선언함으로써 DD에 대해 모호한 가상 함수 f ()를 숨길 것입니다. 그들은 각각 랩퍼 함수 f1 () 및 f2 ()를 각각 정의하고, 각각 클래스-로컬 (비공개) f ()를 호출하여 충돌을 해결합니다. 클래스 DD는 D11 :: f ()를 원하면 f1 ()을 호출하고 D12 :: f ()를 원하면 f2 ()를 호출합니다. 래퍼를 인라인으로 정의하면 오버 헤드가 거의 없습니다.
물론 D11과 D12를 변경할 수 있다면 이러한 클래스 내에서 동일한 트릭을 수행 할 수 있지만 종종 그렇지 않습니다.
다중 상속과 가상 상속에 대해 이미 언급 된 것 외에도 Dr Dobb 's Journal에 대한 흥미로운 기사가 있습니다 : 다중 상속이 유용한 것으로 간주 됨
약간 혼란 스럽습니다. 나는 당신이 몇 가지 개념을 섞고 있는지 모른다.
OP에 가상 기본 클래스가 없습니다. 당신은 기본 수업이 있습니다.
당신은 가상 상속을했습니다. 이것은 일반적으로 다중 상속에서 사용되므로 여러 파생 클래스가 기본 클래스의 멤버를 재생하지 않고 사용합니다.
순수한 가상 함수를 가진 기본 클래스는 인스턴스화되지 않습니다. 이를 위해서는 Paul이 얻는 구문이 필요합니다. 파생 클래스가 해당 함수를 정의해야하므로 일반적으로 사용됩니다.
나는 당신이 요구하는 것을 완전히 얻지 못하기 때문에 이것에 대해 더 이상 설명하고 싶지 않습니다.
가상 함수에 대한 호출이 "오른쪽"클래스로 전달됨을 의미합니다.
C ++ FAQ Lite FTW.
간단히 말해서, 종종 "다이아몬드"계층 구조가 형성되는 다중 상속 시나리오에서 사용됩니다. 그런 다음 가상 상속은 해당 클래스에서 함수를 호출하고 함수가 해당 하위 클래스 위의 클래스 D1 또는 D2로 해석되어야 할 때 하위 클래스에서 작성된 모호성을 깰 것입니다. FAQ 항목을 참조하십시오다이어그램과 세부 사항 을 하십시오.
또한 강력한 기능인 자매 위임 에서도 사용됩니다 (심한 심연은 아님). 참조 이 FAQ를 .
또한 효과적인 C ++ 3 판 (40 판, 2 판)의 항목 40을 참조하십시오.
다이아몬드 상속 실행 가능 사용 예
이 예제는 일반적인 시나리오에서 가상 기본 클래스를 사용하여 다이아몬드 상속을 해결하는 방법을 보여줍니다.
#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 A
gcc 5.4.0 사용. 무엇을해야합니까?
가상 클래스는 아닙니다 가상 상속과 동일. 인스턴스화 할 수없는 가상 클래스, 가상 상속은 완전히 다른 것입니다.
Wikipedia는 내가 할 수있는 것보다 더 잘 설명합니다. http://en.wikipedia.org/wiki/Virtual_inheritance
일반적인 3 단계 비 다이아몬드 비가 상 상속 상속을 사용하면 가장 파생 된 새 객체를 인스턴스화 할 때 new가 호출되고 컴파일러에 의해 클래스 유형에서 객체에 필요한 크기가 확인되어 새로운 객체로 전달됩니다.
새로운 서명이 있습니다 :
_GLIBCXX_WEAK_DEFINITION void *
operator new (std::size_t sz) _GLIBCXX_THROW (std::bad_alloc)
그리고 전화 malloc
하여 void 포인터를 반환합니다.
그런 다음 가장 파생 된 객체의 생성자로 전달되어 즉시 중간 생성자를 호출 한 다음 중간 생성자가 즉시 기본 생성자를 호출합니다. 그런 다음베이스는 객체 시작시 가상 테이블에 대한 포인터를 저장 한 후 그 속성을 저장합니다. 그런 다음 중간 생성자로 돌아가서 가상 테이블 포인터를 동일한 위치에 저장 한 다음 기본 생성자가 저장 한 속성 뒤에 해당 속성을 저장합니다. 가장 파생 된 생성자로 돌아가서 동일한 위치에 가상 테이블에 대한 포인터를 저장 한 다음 중간 생성자가 저장 한 속성 뒤에 해당 속성을 저장합니다.
가상 테이블 포인터를 덮어 쓰므로 가상 테이블 포인터는 항상 가장 파생 된 클래스 중 하나가됩니다. 가상은 가장 파생 된 클래스로 전파되므로 함수가 중간 클래스에서 가상 인 경우 가장 파생 된 클래스에서는 가상이지만 기본 클래스는 아닙니다. 가장 파생 된 클래스의 인스턴스를 기본 클래스에 대한 포인터에 다형성으로 캐스팅하면 컴파일러는이를 가상 테이블에 대한 간접 호출로 해결하지 않고 대신 함수를 직접 호출합니다 A::function()
. 함수가 캐스트 한 유형에 대해 가상 인 경우 항상 가장 파생 된 클래스의 가상 테이블에 대한 호출로 해석됩니다. 해당 유형에 대해 가상이 아닌 경우 호출합니다.Type::function()
객체 포인터를 하여 전달하고 유형으로 캐스트합니다.
실제로 가상 테이블에 대한 포인터를 말할 때 실제로 가상 테이블에 대한 오프셋은 항상 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()
다음 개체 + (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