2016-11-27 5 views
11

मुझे लगता है कि यह कार्यान्वयन-विशिष्ट है, लेकिन armv7, arm64, और x86_64 libstdC++ और libC++ (gcc या clang) का उपयोग करके बनाता है, ऐसा लगता है कि vtables हमेशा शुरुआत में पैडिंग के 8 बाइट्स (64-बिट पर 16) होते हैं, और एक vtable प्राप्त करते समय आम तौर पर इस तरह दिखता है:vtables के आकार (void *) * 0x00 पैडिंग के 2 बाइट क्यों हैं?

ldr.w r0, <address of vtable> 
adds r0, 0x8 
str r0, [r1] ; where r1 is the instance 

और vtable इस तरह दिखता है:

vtable+0x00: 0x00000000 
vtable+0x04: 0x00000000 
vtable+0x08: 0xfirstfunc 
vtable+0x0c: 0xsecondfunc 
vtable+0x10: 0xthirdfunc 

आदि ...

किसी को भी पता है कि ऐसा क्यों है?

+0

व्युत्पन्न कक्षा के लिए तालिका की जांच करें। मैं शर्त लगाता हूं कि सामने * शून्य * शून्य नहीं है। – wallyk

+0

नहीं, मैंने कभी देखा है कि प्रत्येक vtable के लिए, सामने 0 है। व्युत्पन्न वर्ग या नहीं, vtable हमेशा + 8. –

उत्तर

10

शुरुआत में केवल एक शून्य (आकार शून्य *) होना चाहिए (जब तक आरटीटीआई के बिना संकलित नहीं किया जाता)। यह वास्तव में नहीं होना चाहिए लेकिन यह आमतौर पर है, मैं बाद में समझाऊंगा।

vtable के लिए (कम से कम जीसीसी उत्पन्न) कपड़े की तरह दिखता है:

class_offset 
type_info 
first_virtual_function 
second_virtual_function 
etc. 

type_info हो सकता है NULL (0) मामले में कोड RTTI बिना संकलित किया गया।

ऊपर से class_offset बताता है कि आप वहां शून्य क्यों देखते हैं। यह वर्ग वर्ग के भीतर कक्षा ऑफसेट है। अर्थात। होने:

class A { virtual meth() {} }; 
class B { virtual meth() {} }; 
class C: public A, public B { virtual meth() {} }; 

मुख्य वर्ग C में परिणाम होगा, A वर्ग C और B भीतर स्थिति 0 पर प्रारंभिक स्थिति 4 (या 8) वर्ग C भीतर से शुरू।

पॉइंटर वहां है ताकि आप पॉइंटर को मालिक ऑब्जेक्ट में किसी भी क्लास पॉइंटर से पा सकें। तो किसी भी "मुख्य" वर्ग के लिए यह हमेशा 0 होगा लेकिन B वर्ग वर्चुअल तालिका C संदर्भ में मान्य है यह -4 या -8 होगा। आप वास्तव में आम तौर पर अलग से VTables उत्पन्न नहीं करता है संकलक के रूप में सी के लिए vtable (दूसरी छमाही) जांच करने की आवश्यकता:

_ZTV1C: 
    // VTable for C and A within C 
    .quad 0 
    .quad _ZTI1C 
    .quad _ZN1CD1Ev 
    .quad _ZN1CD0Ev 
    .quad _ZN1C4methEv 
    // VTable for B within C 
    .quad -8 
    .quad _ZTI1C 
    .quad _ZThn8_N1CD1Ev 
    .quad _ZThn8_N1CD0Ev 
    .quad _ZThn8_N1C4methEv 

पहले compilers में ऑफसेट मालिक को वास्तविक सूचक गणना करने के लिए इस्तेमाल किया गया था विधि का आह्वान करने से पहले कक्षा। लेकिन जब इसने सीधे मालिक वर्ग पर विधि का आह्वान करते समय मामलों को धीमा कर दिया, तो आधुनिक कंपाइलर्स बल्कि स्टब उत्पन्न करते हैं जो ऑफ़सेट को सीधे घटाता है और विधि के मुख्य कार्यान्वयन पर कूदता है (जैसा कि आप विधि नाम से अनुमान लगा सकते हैं - 8 पर ध्यान दें):

_ZThn8_N1C4methEv: 
    subq $8, %rdi 
    jmp  _ZN1C4methEv 
+0

हू, इसके लिए बहुत बहुत धन्यवाद। यह भी मुझे समझने में मदद करता है कि एकाधिक विरासत के मामलों में गैर वर्चुअल थंक क्यों हैं। वैसे भी, शायद कारण यह है कि बाइनरी के मामले में मैं अलग-अलग हूं, vtable के ऊपर 2 शून्य * है कि आरटीटीआई नहीं है? –

+0

@RyanTerry: हाँ, यह कारण हो सकता है। यदि कोड आरटीटीआई के बिना संकलित किया गया है, तो संरचना को रखा जाता है लेकिन टाइप जानकारी सदस्य न्यूल होता है। मैंने जवाब अपडेट किया। –

संबंधित मुद्दे