2012-02-02 24 views
8
class base { 
public: 
    void virtual fn(int i) { 
     cout << "base" << endl; 
    } 
}; 

class der : public base{ 
    public: 
    void fn(char i) { 
     cout << "der" << endl; 
    } 
}; 

int main() { 

    base* p = new der; 
    char i = 5; 
    p->fn(i); 
    cout << sizeof(base); 
    return 0; 
} 

यहाँ समारोह fn base वर्ग में परिभाषित के हस्ताक्षर समारोह fn()der वर्ग में परिभाषित किया गया है, हालांकि समारोह नाम एक ही है के हस्ताक्षर से अलग है। इसलिए, समारोह der वर्ग में परिभाषित खाल base वर्ग समारोह fn()। इसलिए der एफएन के संस्करण को p->fn(i) कॉल द्वारा कॉल नहीं किया जा सकता है; बस ठीक है।व्युत्पन्न वर्ग वर्चुअल फ़ंक्शन को ओवरराइड नहीं करते समय Vptr क्यों आवश्यक है?

मेरे बिंदु तो यही कारण है कि sizeof वर्ग base या der4 है अगर वहाँ vtable सूचक का कोई उपयोग नहीं है? VTABLE सूचक की आवश्यकता क्या है?

+0

क्या आपने कभी सी ++ में ओवरलोडिंग के बारे में सुना है? –

+0

@KamilKlimek: ओवरलोडिंग विभिन्न हस्ताक्षरों के साथ कई कार्यों को घोषित करने का कार्य है। आप शायद क्या मतलब है ओवरराइडिंग (जो व्युत्पन्न कक्षाओं में विधियों को पुन: कार्यान्वित कर रहा है)। आपकी तरह की जानकारी के लिए –

+0

, ये ओवरलोडेड फ़ंक्शन नहीं हैं। – user966379

उत्तर

6

नोट इस अत्यधिक कार्यान्वयन निर्भर & प्रत्येक संकलक के लिए भिन्न हो सकता है है।

vtable की उपस्थिति की आवश्यकता यह है कि बेस क्लास विरासत और विस्तार के लिए है, और इससे प्राप्त होने वाली कक्षा विधि को ओवरराइड कर सकती है।

दो वर्गों बेस और विभिन्न अनुवाद यूनिट और संकलक में रहते हैं हो सकता है, जबकि बेस वर्ग संकलन वास्तव में पता नहीं होगा यदि विधि या overidden किया जाएगा नहीं व्युत्पन्न। इसलिए, यदि यह कीवर्ड virtual पाता है तो यह vtable उत्पन्न करता है।

+1

लेकिन इस उदाहरण में, सभी कोड * एक ही मॉड्यूल में * है और संकलक जानता था। –

0

विरासत एक is-a रिश्ता है। der is-a base है। base आकार 4 है, der कम से कम आकार 4 होगा। vftableptrbase का एक सदस्य है, यह der के एक सदस्य के लिए किया जाएगा।

base, एक आभासी विधि है तो यह आभासी मेज पर एक सूचक है, चाहे आप इसका इस्तेमाल करते हैं या नहीं होगा।

+0

आप बिंदु खो रहे हैं, क्यू ओपी पूछ रहा है * बेस क्लास '4' का आकार क्यों है? * और नहीं * व्युत्पन्न वर्ग का आकार' 4' क्यों है? * –

+0

यदि व्युत्पन्न कक्षाओं में से कोई भी कभी ओवरराइड नहीं करता है आभासी fucntn –

+0

@ एएलएस मैं उलझन में था क्योंकि यह मेरे लिए बहुत सरल लगता है। इसमें वर्चुअल विधि है, इसके पास vtable के लिए पॉइंटर क्यों नहीं होना चाहिए? मैंने इसे प्रतिबिंबित करने के लिए अपना जवाब संपादित किया। –

1

vtable आमतौर पर वर्चुअल फ़ंक्शंस के लिए उपयोग नहीं किया जाता है, लेकिन जब आप कुछ dynamic_cast करते हैं या कक्षा के लिए type_info तक पहुंचते हैं तो कक्षा वर्ग की पहचान करने के लिए भी इसका उपयोग किया जाता है।

यदि कंपाइलर का पता चलता है कि कोई वर्चुअल फ़ंक्शंस कभी ओवरराइड नहीं होता है और अन्य सुविधाओं में से कोई भी उपयोग नहीं किया जाता है, तो यह ऑप्टिमाइज़ेशन के रूप में vtable पॉइंटर को हटा सकता है।

स्पष्ट रूप से संकलक लेखक को ऐसा करने में परेशानी नहीं मिली है। शायद क्योंकि इसका उपयोग अक्सर नहीं किया जाएगा, और क्योंकि आप बेस क्लास से virtual को हटाकर इसे स्वयं कर सकते हैं।

+0

एक आभासी बी (int), बी: सार्वजनिक ए - कोई ओवरराइड नहीं, सी: सार्वजनिक बी ओवरराइड बी (int), डी: सार्वजनिक सी - कोई ओवरराइड नहीं। और आप इस मामले में व्यवहार करने के लिए संकलक को छोड़कर कैसे करते हैं? Vtable कहाँ होना चाहिए और कहाँ नहीं होना चाहिए? अगर मैं बी * उदाहरण = नया डी; ? –

+0

क्या प्लगइन फैक्ट्री के रूप में इसका उपयोग किया जाता है? इसका पता लगाने का कोई भौतिक तरीका नहीं है, अगर विरासत विधि को ओवरराइड करने वाले विरासत वर्ग में होगा या नहीं होगा। –

+0

यदि कहीं भी ओवरराइड है, तो Vtable को हर जगह वहां होना होगा, क्योंकि प्रत्येक वर्ग का केवल एक संस्करण ही हो सकता है। यह ठीक है अगर (बड़ा अगर!) संकलक यह सुनिश्चित कर सकता है कि इसकी आवश्यकता नहीं है, इसे अनुकूलित किया जा सकता है। –

1

संकलक 'आधार' कक्षा से बाहर vtable सदस्य चर बाहर अनुकूलित नहीं कर सकते, क्योंकि वहाँ एक ही है या किसी अन्य परियोजना के भीतर एक और स्रोत फ़ाइल जो निम्नलिखित शामिल हैं हो सकता है:

struct ived : base { 
    ived() : p(new char[BIG_DATA_SIZE]) {} 
    virtual ~ived(); 
    virtual void fn(int); 
private: 
    char* p; 
}; 

नाशक और fn सकता है कहीं और लागू किया:

0:

ived::~ived() { delete[] p; } 

void ived::fn(int) { 
    cout << "ived" << endl; 
} 

और एक और जगह में कहीं इस तरह कोड हो सकता है

तो, दो समस्याएं होंगी: वर्चुअल फ़ंक्शन ived::fn नहीं कहा जाता है, आभासी विनाशक नहीं कहा जाता है, इसलिए BIG_DATA_SIZE हटाया नहीं गया है। अन्यथा, sizeof(base) यहां अलग होगा। यही कारण है कि संकलक हमेशा वर्चुअल सदस्य फ़ंक्शन या वर्चुअल बेस क्लास वाले किसी भी वर्ग के लिए vtable उत्पन्न करते हैं।

व्युत्पन्न कक्षाओं में विनाशकों को बुलाए जाने के संबंध में, इसे जरूरी माना जाना चाहिए: यदि आपके पास किसी भी वर्चुअल फ़ंक्शन के साथ कोई वर्ग है, तो वह वर्ग आभासी विनाशक घोषित करेगा।