C ++ में, वर्चुअल बेस क्लास क्या है?


403

मैं जानना चाहता हूं कि " वर्चुअल बेस क्लास " क्या है और इसका क्या मतलब है।

मुझे एक उदाहरण दिखाते हैं:

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

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

क्या हमें 'मल्टीपल इनहेरिटेंस' में वर्चुअल बेस क्लास का इस्तेमाल करना चाहिए क्योंकि अगर क्लास ए में मेंबर वेरिएंट ए और क्लास बी में मेंबर ए है और क्लास सी में क्लास ए और बी है तो हम कैसे तय करते हैं कि किस 'ए' को इस्तेमाल करना है?
नमित सिन्हा

2
@NamitSinha नहीं, वर्चुअल इनहेरिटेंस उस समस्या को हल नहीं करता है। सदस्य वैसे भी अस्पष्ट होगा
इचथियो

@NamitSinha वर्चुअल इनहेरिटेंस कई वंशानुगत संबंधित अस्पष्टताओं को हटाने के लिए एक जादुई उपकरण नहीं है। यह एक बार से अधिक अप्रत्यक्ष आधार होने की "समस्या" हल करता है। जो केवल एक समस्या है अगर इसे साझा करने का इरादा था (अक्सर लेकिन हमेशा मामला नहीं)।
जिज्ञासु

जवाबों:


533

वर्चुअल बेस क्लास, वर्चुअल इनहेरिटेंस में उपयोग किया जाता है, एक विरासत श्रेणी के कई "उदाहरणों" को रोकने का एक तरीका है जो कई विरासतों का उपयोग करते समय विरासत वंशानुक्रम में दिखाई देता है।

इस परिदृश्य पर विचार करें:

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

यह एक छोटा सारांश है। अधिक जानकारी के लिए, इसे और इसे पढ़ें । एक अच्छा उदाहरण भी यहाँ उपलब्ध है


7
@Bohdan नहीं यह नहीं है :)
OJ।

6
@OJ। क्यों नहीं? वे प्रफुल्लित हैं :)
बोहदन

15
@ बोहदन कम से कम वर्चुअल कीवर्ड का उपयोग करते हैं, क्योंकि जब हम वर्चुअल कीवर्ड का उपयोग करते हैं, तो एक भारी वजन तंत्र लागू होता है। तो, आपकी प्रोग्राम दक्षता कम हो जाएगी।
सागर

73
आपका "खूंखार हीरा" आरेख भ्रामक है, हालांकि यह आमतौर पर उपयोग किया जाता है। - यह वास्तव में वर्ग विरासत संबंधों को दर्शाने वाला एक चित्र है नहीं एक वस्तु लेआउट। भ्रामक हिस्सा यह है कि अगर हम उपयोग करते हैं virtual, तो ऑब्जेक्ट लेआउट हीरे की तरह दिखता है; और यदि हम उपयोग नहीं करते हैं, virtualतो ऑब्जेक्ट लेआउट एक पेड़ की संरचना की तरह दिखता है जिसमें दो As
MM

5
मुझे इस उत्तर को एमएम द्वारा बताए गए कारण के लिए छोड़ना होगा - आरेख पोस्ट के विपरीत व्यक्त करता है।
डेविड स्टोन

251

मेमोरी लेआउट के बारे में

साइड नोट के रूप में, ड्रेडेड डायमंड के साथ समस्या यह है कि बेस क्लास कई बार मौजूद होता है। तो नियमित विरासत के साथ, आपको लगता है कि आपके पास है:

  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% संभावना है कि उक्त पदानुक्रम में कुछ गड़बड़ है।


आपका 'क्या गलत हो सकता है' आधार सदस्य तक सीधी पहुंच के कारण है, न कि कई उत्तराधिकारियों के कारण। 'बी' से छुटकारा पाएं और आपको एक ही समस्या है। मूल नियम: 'यदि इसका निजी नहीं है, तो इसे आभासी होना चाहिए' समस्या से बचा जाता है। m_iValue आभासी नहीं है और इसके लिए निजी होना चाहिए
क्रिस डोड

4
@ क्रिस डोड: बिल्कुल नहीं। M_iValue के साथ जो भी होता है, वह किसी भी प्रतीक ( जैसे टाइपडिफ , सदस्य चर, सदस्य फ़ंक्शन, बेस क्लास के लिए कलाकारों आदि ) के साथ हुआ होगा। यह वास्तव में एक मल्टीपल इनहेरिटेंस इश्यू है, एक ऐसा मुद्दा जिसे यूजर्स को कई तरह से सही तरीके से यूज करने के बारे में पता होना चाहिए, बजाय इसके कि वह जावा तरीके से जाए और यह निष्कर्ष निकाले कि "मल्टीपल इनहेरिटेंस 100% बुराई है, चलो इंटरफेस के साथ ऐसा करते हैं"।
पिरसेबल

नमस्ते, जब हम वर्चुअल कीवर्ड का उपयोग करते हैं, तो ए की केवल एक प्रति होगी। मेरा सवाल यह है कि हमें कैसे पता चलेगा कि यह बी या सी से आ रहा है? क्या मेरा प्रश्न बिल्कुल वैध है?
user875036

@ user875036: A, B और C. दोनों से आ रहा है। वास्तव में, आभासीता कुछ चीजों को बदल देती है (जैसे D, A के निर्माता को बुलाएगा, B और न ही C)। B और C (और D) दोनों का एक सूचक है
पियरसबल

3
एफडब्ल्यूआईडब्ल्यू, किसी की सोच के मामले में, सदस्य चर आभासी नहीं हो सकते हैं - आभासी कार्यों के लिए एक विशिष्ट है । एसओ संदर्भ: stackoverflow.com/questions/3698831/…
rholmes

34

वर्चुअल बेस के साथ कई-विरासत को समझाते हुए C ++ ऑब्जेक्ट मॉडल के ज्ञान की आवश्यकता होती है। और विषय को स्पष्ट रूप से समझाते हुए एक लेख में सबसे अच्छा किया जाता है और टिप्पणी बॉक्स में नहीं।

सबसे अच्छा, पठनीय स्पष्टीकरण जो मैंने पाया कि इस विषय पर मेरे सभी संदेह हल हो गए, यह लेख था: http://www.phpcompiler.org/articles/virtualinheritance.html

आपको वास्तव में विषय पर कुछ और पढ़ने की आवश्यकता नहीं होगी (जब तक कि आप एक संकलक लेखक नहीं हैं) पढ़ने के बाद ...


10

वर्चुअल बेस क्लास एक ऐसा वर्ग है जिसे तत्काल नहीं बनाया जा सकता है: आप इसमें से प्रत्यक्ष ऑब्जेक्ट नहीं बना सकते हैं।

मुझे लगता है कि आप दो बहुत अलग चीजों को भ्रमित कर रहे हैं। वर्चुअल इनहेरिटेंस अमूर्त वर्ग के समान नहीं है। वर्चुअल इनहेरिटेंस फ़ंक्शन कॉल के व्यवहार को संशोधित करता है; कभी-कभी यह फ़ंक्शन कॉल को हल करता है जो अन्यथा अस्पष्ट होगा, कभी-कभी यह फ़ंक्शन कॉल को एक वर्ग के अलावा किसी अन्य से संभालता है जो गैर-आभासी विरासत में उम्मीद करेगा।


7

मैं OJ की तरह स्पष्टीकरण में जोड़ना चाहूंगा।

आभासी विरासत एक मूल्य के बिना नहीं आती है। आभासी सभी चीजों की तरह, आपको एक प्रदर्शन हिट मिलता है। इस प्रदर्शन हिट के आसपास एक तरीका है जो संभवतः कम सुरुचिपूर्ण है।

वस्तुतः प्राप्त करके हीरे को तोड़ने के बजाय, आप हीरे की एक और परत जोड़ सकते हैं, कुछ इस तरह से प्राप्त करने के लिए:

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

सभी वर्गों में से कोई भी वस्तुतः विरासत में नहीं मिलता है। कक्षा D21 और D22 तब वर्चुअल फ़ंक्शन f () को छिपाएंगे जो DD के लिए अस्पष्ट है, शायद फ़ंक्शन को निजी घोषित करके। वे प्रत्येक को क्रमशः एक रैपर फंक्शन, f1 () और f2 (), प्रत्येक कॉलिंग क्लास-लोकल (प्राइवेट) f () परिभाषित करते हैं, इस प्रकार संघर्षों को हल करते हैं। क्लास डीडी f1 () को कॉल करता है अगर वह D11 :: f () और f2 () चाहता है तो वह D12 :: f () चाहता है। यदि आप रैपर इनलाइन को परिभाषित करते हैं तो आपको लगभग शून्य ओवरहेड मिल जाएगा।

बेशक, यदि आप D11 और D12 को बदल सकते हैं तो आप इन वर्गों के अंदर एक ही चाल कर सकते हैं, लेकिन अक्सर ऐसा नहीं होता है।


2
यह कम या ज्यादा सुरुचिपूर्ण या हल करने वाली अस्पष्टताओं का मामला नहीं है (आप हमेशा स्पष्ट xxx :: विनिर्देशों का उपयोग कर सकते हैं)। गैर-आभासी विरासत के साथ, कक्षा डीडी के प्रत्येक उदाहरण में बी के दो स्वतंत्र उदाहरण हैं। जैसे ही कक्षा में एक एकल गैर-स्थैतिक डेटा सदस्य होता है, आभासी और गैर-आभासी विरासत केवल वाक्यविन्यास से अधिक भिन्न होते हैं।
user3489112

@ user3489112 जैसे ही ... कुछ भी नहीं। आभासी और गैर आभासी विरासत शब्दार्थिक रूप से भिन्न होते हैं, अवधि।
जिज्ञासु


1

आप थोड़े भ्रमित हो रहे हैं। मुझे नहीं पता कि क्या आप कुछ अवधारणाओं को मिला रहे हैं।

आपके पास अपने ओपी में वर्चुअल बेस क्लास नहीं है। आपके पास बस एक आधार वर्ग है।

आपने वर्चुअल इनहेरिटेंस किया। यह आमतौर पर कई वंशानुक्रम में उपयोग किया जाता है ताकि कई व्युत्पन्न वर्ग आधार वर्ग के सदस्यों को उनका पुनरुत्पादन किए बिना उपयोग करें।

एक शुद्ध आभासी फ़ंक्शन के साथ एक आधार वर्ग को तत्काल नहीं किया जाता है। इसके लिए सिंटैक्स की आवश्यकता होती है जो पॉल को मिलता है। यह आमतौर पर उपयोग किया जाता है ताकि व्युत्पन्न वर्ग को उन कार्यों को परिभाषित करना पड़े।

मैं इसके बारे में कोई और व्याख्या नहीं करना चाहता क्योंकि मैं पूरी तरह से नहीं मिलता जो आप पूछ रहे हैं।


1
एक "बेस क्लास" जिसे वर्चुअल इनहेरिटेंस में उपयोग किया जाता है, वह "वर्चुअल बेस क्लास" बन जाता है (उस सटीक विरासत के संदर्भ में)।
ल्यूक हरमिट

1

इसका मतलब है कि आभासी फ़ंक्शन के लिए कॉल "दाएं" वर्ग में भेजा जाएगा।

सी ++ एफएक्यू लाइट एफटीडब्ल्यू।

संक्षेप में, इसका उपयोग अक्सर कई-विरासत परिदृश्यों में किया जाता है, जहां "हीरा" पदानुक्रम बनता है। वर्चुअल इनहेरिटेंस तब निचले वर्ग में निर्मित अस्पष्टता को तोड़ देगा, जब आप उस कक्षा में फ़ंक्शन को कॉल करते हैं और फ़ंक्शन को उस निचले वर्ग के ऊपर या तो कक्षा डी 1 या डी 2 को हल करने की आवश्यकता होती है। देखें पूछे जाने वाले प्रश्न आइटम एक चित्र और जानकारी के लिए।

इसका उपयोग बहन के प्रतिनिधिमंडल में भी किया जाता है , एक शक्तिशाली विशेषता (हालांकि दिल की बेहोशी के लिए नहीं)। यह FAQ देखें ।

आइटम 40 को प्रभावी C ++ 3rd संस्करण में भी देखें (43 वें संस्करण में 43)।


1

डायमंड इनहेरिटेंस रननेबल यूसेज उदाहरण

यह उदाहरण दिखाता है कि विशिष्ट परिदृश्य में वर्चुअल बेस क्लास का उपयोग कैसे करें: हीरे की विरासत को हल करने के लिए।

#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);मुख्य समारोह से मुझे एक संकलन त्रुटि मिलती है: aDefault is not a member of Agcc 5.4.0 का उपयोग करना। यह क्या करने के लिए लगता है?
सेबनाग

@SebTu आह धन्यवाद, बस कुछ मैं कॉपी पेस्ट से हटाना भूल गया, अब इसे हटा दिया। इसके बिना भी उदाहरण सार्थक होना चाहिए।
सिरो सेंटिल्ली 郝海东 冠状 iro i ''

0

वर्चुअल कक्षाएं वर्चुअल इनहेरिटेंस के समान नहीं हैं । वर्चुअल कक्षाएं जिन्हें आप तुरंत नहीं कर सकते हैं, आभासी विरासत पूरी तरह से कुछ और है।

विकिपीडिया मुझे इससे बेहतर बताता है। http://en.wikipedia.org/wiki/Virtual_inheritance


6
C ++ में "वर्चुअल क्लास" जैसी कोई चीज नहीं है। हालांकि "वर्चुअल बेस क्लास" हैं जो किसी दिए गए वंशानुक्रम के बारे में "वर्चुअल" हैं। आप जो उल्लेख करते हैं वह आधिकारिक रूप से "अमूर्त वर्ग" कहलाता है।
ल्यूक हरमीत

@LucHermitte, C ++ में निश्चित रूप से आभासी कक्षाएं हैं। इस जाँच करें: en.wikipedia.org/wiki/Virtual_class
रफीद

"त्रुटि: 'आभासी' केवल कार्यों के लिए निर्दिष्ट किया जा सकता है"। मुझे नहीं पता कि यह कौन सी भाषा है। लेकिन C ++ में वर्चुअल क्लास जैसी कोई चीज निश्चित रूप से नहीं है।
ल्यूक हरमिट

0

नियमित वंशानुक्रम

ठेठ 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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.