मैं जानना चाहता हूं कि " वर्चुअल बेस क्लास " क्या है और इसका क्या मतलब है।
मुझे एक उदाहरण दिखाते हैं:
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 का एक उदाहरण B से बना होगा, जिसमें A शामिल है, और C में A भी शामिल है। इसलिए आपके पास A के दो "उदाहरण" (बेहतर अभिव्यक्ति के लिए) हैं।
जब आपके पास यह परिदृश्य होता है, तो आपको अस्पष्टता की संभावना होती है। ऐसा करने पर क्या होता है:
D d;
d.Foo(); // is this B's Foo() or C's Foo() ??
इस समस्या को हल करने के लिए वर्चुअल इनहेरिटेंस है। जब आप अपनी कक्षाओं को विरासत में देते समय वर्चुअल निर्दिष्ट करते हैं, तो आप संकलक को बता रहे हैं कि आप केवल एक ही उदाहरण चाहते हैं।
class A { public: void Foo() {} };
class B : public virtual A {};
class C : public virtual A {};
class D : public B, public C {};
इसका मतलब है कि पदानुक्रम में शामिल ए का केवल एक "उदाहरण" है। इसलिये
D d;
d.Foo(); // no longer ambiguous
यह एक छोटा सारांश है। अधिक जानकारी के लिए, इसे और इसे पढ़ें । एक अच्छा उदाहरण भी यहाँ उपलब्ध है ।
virtual
, तो ऑब्जेक्ट लेआउट हीरे की तरह दिखता है; और यदि हम उपयोग नहीं करते हैं, virtual
तो ऑब्जेक्ट लेआउट एक पेड़ की संरचना की तरह दिखता है जिसमें दो A
s
साइड नोट के रूप में, ड्रेडेड डायमंड के साथ समस्या यह है कि बेस क्लास कई बार मौजूद होता है। तो नियमित विरासत के साथ, आपको लगता है कि आपके पास है:
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
यह एक पर्यवेक्षक पैटर्न (उदाहरण के लिए m_iValue
) की तरह कुछ शांत सुविधा को जोड़ता है ।D
इनहेरिट B
और से C
, और इस तरह से A
।सामान्य विरासत के साथ, संशोधित करने m_iValue
से D
अस्पष्ट है और यह समाधान किया जाना चाहिए। यहां तक कि अगर यह है, तो दो m_iValues
अंदर हैं 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 तब वर्चुअल फ़ंक्शन f () को छिपाएंगे जो DD के लिए अस्पष्ट है, शायद फ़ंक्शन को निजी घोषित करके। वे प्रत्येक को क्रमशः एक रैपर फंक्शन, f1 () और f2 (), प्रत्येक कॉलिंग क्लास-लोकल (प्राइवेट) f () परिभाषित करते हैं, इस प्रकार संघर्षों को हल करते हैं। क्लास डीडी f1 () को कॉल करता है अगर वह D11 :: f () और f2 () चाहता है तो वह D12 :: f () चाहता है। यदि आप रैपर इनलाइन को परिभाषित करते हैं तो आपको लगभग शून्य ओवरहेड मिल जाएगा।
बेशक, यदि आप D11 और D12 को बदल सकते हैं तो आप इन वर्गों के अंदर एक ही चाल कर सकते हैं, लेकिन अक्सर ऐसा नहीं होता है।
मल्टीपल और वर्चुअल इनहेरिटेंस (ओं) के बारे में जो पहले ही कहा जा चुका है, उसमें डॉ। डॉब की पत्रिका पर एक बहुत ही दिलचस्प लेख है: मल्टीपल इनहेरिटेंस माना गया उपयोगी
आप थोड़े भ्रमित हो रहे हैं। मुझे नहीं पता कि क्या आप कुछ अवधारणाओं को मिला रहे हैं।
आपके पास अपने ओपी में वर्चुअल बेस क्लास नहीं है। आपके पास बस एक आधार वर्ग है।
आपने वर्चुअल इनहेरिटेंस किया। यह आमतौर पर कई वंशानुक्रम में उपयोग किया जाता है ताकि कई व्युत्पन्न वर्ग आधार वर्ग के सदस्यों को उनका पुनरुत्पादन किए बिना उपयोग करें।
एक शुद्ध आभासी फ़ंक्शन के साथ एक आधार वर्ग को तत्काल नहीं किया जाता है। इसके लिए सिंटैक्स की आवश्यकता होती है जो पॉल को मिलता है। यह आमतौर पर उपयोग किया जाता है ताकि व्युत्पन्न वर्ग को उन कार्यों को परिभाषित करना पड़े।
मैं इसके बारे में कोई और व्याख्या नहीं करना चाहता क्योंकि मैं पूरी तरह से नहीं मिलता जो आप पूछ रहे हैं।
इसका मतलब है कि आभासी फ़ंक्शन के लिए कॉल "दाएं" वर्ग में भेजा जाएगा।
सी ++ एफएक्यू लाइट एफटीडब्ल्यू।
संक्षेप में, इसका उपयोग अक्सर कई-विरासत परिदृश्यों में किया जाता है, जहां "हीरा" पदानुक्रम बनता है। वर्चुअल इनहेरिटेंस तब निचले वर्ग में निर्मित अस्पष्टता को तोड़ देगा, जब आप उस कक्षा में फ़ंक्शन को कॉल करते हैं और फ़ंक्शन को उस निचले वर्ग के ऊपर या तो कक्षा डी 1 या डी 2 को हल करने की आवश्यकता होती है। देखें पूछे जाने वाले प्रश्न आइटम एक चित्र और जानकारी के लिए।
इसका उपयोग बहन के प्रतिनिधिमंडल में भी किया जाता है , एक शक्तिशाली विशेषता (हालांकि दिल की बेहोशी के लिए नहीं)। यह FAQ देखें ।
आइटम 40 को प्रभावी C ++ 3rd संस्करण में भी देखें (43 वें संस्करण में 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 A
gcc 5.4.0 का उपयोग करना। यह क्या करने के लिए लगता है?
वर्चुअल कक्षाएं वर्चुअल इनहेरिटेंस के समान नहीं हैं । वर्चुअल कक्षाएं जिन्हें आप तुरंत नहीं कर सकते हैं, आभासी विरासत पूरी तरह से कुछ और है।
विकिपीडिया मुझे इससे बेहतर बताता है। http://en.wikipedia.org/wiki/Virtual_inheritance
ठेठ 3 स्तर के गैर-हीरा गैर-आभासी-विरासत विरासत के साथ, जब आप एक नई सबसे अधिक व्युत्पन्न-वस्तु को हटाते हैं, तो नए को बुलाया जाता है और ऑब्जेक्ट के लिए आवश्यक आकार को कंपाइलर द्वारा वर्ग प्रकार से हल किया जाता है और नया पास किया जाता है।
नए में एक हस्ताक्षर है:
_GLIBCXX_WEAK_DEFINITION void *
operator new (std::size_t sz) _GLIBCXX_THROW (std::bad_alloc)
और malloc
शून्य सूचक को वापस करने के लिए एक कॉल करता है
इसके बाद सबसे व्युत्पन्न ऑब्जेक्ट के कंस्ट्रक्टर को पास किया जाता है, जो तुरंत मध्य कंस्ट्रक्टर को कॉल करेगा और फिर मध्य कंस्ट्रक्टर तुरंत बेस कंस्ट्रक्टर को कॉल करेगा। आधार तब वस्तु की शुरुआत में अपनी आभासी तालिका में एक पॉइंटर को संग्रहीत करता है और उसके बाद उसके गुण। इसके बाद मध्य कंस्ट्रक्टर में वापस आ जाता है जो अपने वर्चुअल टेबल पॉइंटर को उसी स्थान पर स्टोर करेगा और उसके बाद बेस कंस्ट्रक्टर द्वारा स्टोर की गई विशेषताओं के बाद इसकी विशेषताएं। यह सबसे अधिक व्युत्पन्न निर्माणकर्ता के पास लौटता है, जो एक पॉइंटर को उसी स्थान पर अपनी वर्चुअल टेबल पर संग्रहीत करता है और फिर उसके गुणों के बाद की विशेषताओं को मध्य कंस्ट्रक्टर द्वारा संग्रहीत किया जाता है।
क्योंकि वर्चुअल टेबल पॉइंटर को ओवरराइट किया गया है, वर्चुअल टेबल पॉइंटर हमेशा सबसे व्युत्पन्न वर्ग में से एक होता है। आभासीता सबसे व्युत्पन्न वर्ग की ओर फैलती है इसलिए यदि कोई फ़ंक्शन मध्यम वर्ग में आभासी है, तो यह सबसे व्युत्पन्न वर्ग में आभासी होगा, लेकिन आधार वर्ग नहीं। यदि आप पॉलीमॉर्फिक रूप से सबसे व्युत्पन्न वर्ग का एक उदाहरण बेस क्लास के लिए एक पॉइंटर में डालते हैं तो कंपाइलर इसे वर्चुअल टेबल पर एक अप्रत्यक्ष कॉल पर हल नहीं करेगा और इसके बजाय सीधे फ़ंक्शन को कॉल करेगा 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 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
उस क्लास में प्रतिनिधित्व करने के लिए पॉइंटर्स टू ऑफसेट के साथ ऑब्जेक्ट में बदल देता है ।
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