2015-12-09 10 views
6

मैं सी ++ 14 §3.11/2 में उदाहरण का उपयोग कर रहा:जब मैंने वर्चुअल बेस से डी प्राप्त किया था तो आकार (डी) ने वीएस2015 में 8 बाइट्स क्यों बढ़ाए?

struct B { long double d; }; 
struct D : virtual B { char c; } 

बजना, जी में नीचे दिए गए स्निपेट चल ++ और VS2015

#include <iostream> 
struct B { long double d; }; 
struct D : /*virtual*/ B { char c; }; 

int main() 
{ 
    std::cout << "sizeof(long double) = " << sizeof(long double) << '\n'; 
    std::cout << "alignof(long double) = " << alignof(long double) << '\n'; 

    std::cout << "sizeof(B) = " << sizeof(B) << '\n'; 
    std::cout << "alignof(B) = " << alignof(B) << '\n'; 

    std::cout << "sizeof(D) = " << sizeof(D) << '\n'; 
    std::cout << "alignof(D) = " << alignof(D) << '\n'; 
} 

बाद मैं निम्नलिखित परिणाम मिल गया:

      clang   g++   VS2015 
sizeof(long double)  16    16   8 
alignof(long double)  16    16   8 
sizeof(B)     16    16   8 
alignof(B)     16    16   8 
sizeof(D)     32    32   16 
alignof(D)     16    16   8 

अब, उपरोक्त कोड में struct D की परिभाषा में virtual uncommenting और बजना, जी ++ और VS20 के लिए फिर से कोड चलाने के बाद

      clang   g++   VS2015 
sizeof(long double)  16    16   8 
alignof(long double)  16    16   8 
sizeof(B)     16    16   8 
alignof(B)     16    16   8 
sizeof(D)     32    32   24 
alignof(D)     16    16   8 

मैं ऊपर प्राप्त परिणामों के बारे में कोई संदेह नहीं है, एक ही अपवाद के साथ: 15, मैं निम्नलिखित परिणाम प्राप्त क्यों किया sizeof(D) VS2015 में 24 16 से वृद्धि हुई है?

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

+0

इन structs @NathanOliver आभासी कार्यों जरूरत नहीं है। जब आप वस्तुतः प्राप्त करते हैं तो क्या vtable बनाया जाता है? – Alex

+0

बस कंपाइलर से आपको बताने के लिए कहें, '/ d1reportAllClassLayout' संकलन विकल्प का उपयोग करें। आप बी :: डी == 24 के लिए डी :: सी + 7 बाइट पैडिंग + 8 बाइट्स के लिए वर्चुअल बेस टेबल पॉइंटर + 1 बाइट के लिए 8 बाइट देखते हैं। पॉइंटर को गैर वर्चुअल केस में ऑप्टिमाइज़ किया गया है। –

+0

मुझे लगता है कि यह [ब्लॉग पोस्ट] (http://lolengine.net/blog/2012/10/21/the-stolen-bytes) आपकी मदद करता है। टिप्पणी में, जंगल, जिन्होंने अतीत में एमएसवीसी कंपाइलर लिखा था, एक स्पष्टीकरण देता है। – akakatak

उत्तर

1

यदि आप वास्तव में वर्चुअल विरासत के आभासी पहलू का उपयोग करते हैं, तो मुझे लगता है कि vtable पॉइंटर की आवश्यकता स्पष्ट हो जाती है। Vtable में एक आइटम D की शुरुआत से B की शुरुआत की ऑफसेट होने की संभावना है।

मान लें E विरासत में दोनों E से B और F वस्तुतः से विरासत में मिली और D ऐसी है कि D एक F अंदर इसके आधार वर्ग के लिए BE अंदर का उपयोग कर समाप्त होता है। D की एक विधि में यह नहीं पता कि यह F का बेस क्लास है। आप B के सदस्यों को vtable में संग्रहीत जानकारी के बिना कैसे ढूंढ सकते हैं?

तो क्लैंग और जी ++ ने एक vtable सूचक में पैडिंग के 8 बाइट बदल दिए और आपने सोचा कि कोई बदलाव नहीं हुआ है। लेकिन वीएस2015 में कभी भी वह पैडिंग नहीं थी, इसलिए इसे vtable सूचक के लिए 8 बाइट जोड़ने की आवश्यकता थी।

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

+0

मुझे लगता है कि यह एक उचित उत्तर है: 'तो क्लैंग और जी ++ ने एक vtable सूचक में पैडिंग के 8 बाइट बदल दिए और आपने सोचा कि कोई बदलाव नहीं आया है। लेकिन वीएस2015 में कभी भी वह पैडिंग नहीं थी, इसलिए इसे vtable पॉइंटर के लिए 8 बाइट जोड़ने की आवश्यकता थी। धन्यवाद (+1)। – Ayrosa

+0

मुझे लगता है कि ज्यादातर लोग सवाल देख रहे हैं, इसका मतलब यह होगा कि "आभासी कार्यों के बिना वर्चुअल विरासत को अंतरिक्ष को ले जाने वाले किसी चीज़ को सामान्य रूप से ले जाने की आवश्यकता होती है"। तो मुझे लगता है कि जो हिस्सा आप "उत्तर" के रूप में देखते हैं, अधिकांश लोगों के लिए उत्तर के बीच कनेक्शन और प्रश्न के उदाहरण के अंदर कुछ विवरणों के बारे में एक स्पष्टीकरण है। मुझे खुशी है कि आपको ** आपका ** उत्तर मिला है, लेकिन अगर आप उस पर कटौती का सुझाव दे रहे हैं, तो मुझे नहीं लगता। – JSF

0

जब virtual बेस ऑब्जेक्ट होता है, तो व्युत्पन्न ऑब्जेक्ट के पते से संबंधित बेस ऑब्जेक्ट का स्थान स्थिर रूप से पूर्वानुमानित नहीं होता है। विशेष रूप से, अगर आप अपने वर्ग पदानुक्रम थोड़ा विस्तार यह स्पष्ट हो जाता है कि एक से अधिक D subobjects जो अभी भी सिर्फ एक B आधार वस्तु को संदर्भित करने की जरूरत हो सकता है:

class I1: public D {}; 
class I2: public D {}; 
class Most: public I1, public I2 {}; 

आप या तो परिवर्तित करके एक Most वस्तु से एक D* प्राप्त कर सकते हैं पहले I1 करने के लिए या पहले I2 रहे हैं:

Most m; 
D* d1 = static_cast<I1*>(&m); 
D* d2 = static_cast<I2*>(&m); 

आप, d1 != d2, यानी, वहाँ वास्तव में दो D subobjects, लेकिन static_cast<B*>(d1) == static_cast<B*>(d2), यानी हैं होगा वहाँ j है ust एक B subobject। उपरोक्त को गतिशील ऑफसेट की आवश्यकता होने के लिए d1 और d2 को समायोजित करने के तरीके को निर्धारित करने के लिए। इस ऑफ़सेट को निर्धारित करने के तरीके के बारे में जानकारी कहीं भी संग्रहीत की जानी चाहिए। इस जानकारी के लिए भंडारण अतिरिक्त 8 बाइट्स का संभावित स्रोत है।

मुझे नहीं लगता कि एमएसवीसी ++ में प्रकारों के लिए ऑब्जेक्ट लेआउट [सार्वजनिक रूप से] दस्तावेज है, यानी, यह सुनिश्चित करना असंभव है कि वे क्या कर रहे हैं। इसके दिखने से वे 64 बिट ऑब्जेक्ट को एम्बेड करने में सक्षम होने के लिए कह सकते हैं कि बेस ऑब्जेक्ट व्युत्पन्न ऑब्जेक्ट के पते से संबंधित रहता है (किसी प्रकार की जानकारी के लिए पॉइंटर, बेस के लिए पॉइंटर, आधार पर ऑफ़सेट, कुछ उसके जैसा)। अन्य 8 बाइट सबसे अधिक संभावना char स्टोर करने की आवश्यकता से उत्पन्न होती है और कुछ पैडिंग को ऑब्जेक्ट को उपयुक्त सीमा पर गठबंधन करने की आवश्यकता होती है। ऐसा लगता है कि अन्य दो कंपाइलर्स क्या करते हैं, इसके अलावा वे long double के लिए 16 बाइट्स का उपयोग करते हैं, (संभवतः यह केवल 10 बाइट उपयुक्त संरेखण के लिए गद्देदार है)।

यह समझने के लिए कि सी ++ ऑब्जेक्ट मॉडल कैसे काम कर सकता है, आप शायद स्टैन लिप्पमैन के "Inside the C++ Object Model" पर एक नज़र डालना चाहें। यह थोड़ा दिनांकित है लेकिन संभावित कार्यान्वयन तकनीकों का वर्णन करता है। चाहे एमएसवीसी ++ उनमें से किसी का उपयोग करता है, मुझे नहीं पता लेकिन यह विचार देता है कि इसका क्या उपयोग किया जा सकता है।

gcc और clang द्वारा प्रयोग किया जाता आप Itanium ABI पर एक नज़र हो सकता है ऑब्जेक्ट मॉडल के लिए: वे अनिवार्य रूप से वास्तव में इस्तेमाल किया सीपीयू को मामूली समायोजन के साथ इटेनियम ABI का उपयोग।

0

दृश्य स्टूडियो में, डिफ़ॉल्ट व्यवहार यह है कि सभी structs 8 बाइट सीमा के साथ गठबंधन होते हैं। i.e.even अगर आप

struct A { 
    char c; 
} 

और फिर sizeof(A) जाँच, आप देखेंगे कि यह 8 बाइट्स देखेंगे।

अब, आपके मामले में, जब आपने वर्चुअल होने के लिए स्ट्रक्चरल डी के विरासत के प्रकार को बदल दिया है, तो संकलक को इसे पूरा करने के लिए कुछ अतिरिक्त करना होगा। सबसे पहले, यह संरचना डी के लिए वर्चुअल टेबल बनाता है। इस vtable में क्या होता है? इसमें मेमोरी में स्ट्रक्चरल बी के ऑफसेट के लिए एक पॉइंटर होता है। अगला, यह स्ट्रक्चर डी के सिर पर एक वीपीआरआर जोड़ता है जो नव निर्मित vtable को इंगित करता है।

इसलिए, अब struct डी दिखना चाहिए:

struct D : virtual B { void* vptr; char c; } 

तो, डी के आकार होगा:

sizeof (long double) + sizeof (void*) + sizeof (char) = 8 + 8 + 1 = 17 

यह वह जगह है जहां सीमा संरेखण आरंभ में हुई चर्चा में आता है। चूंकि सभी structs को 8 बाइट सीमा से गठबंधन किया जाना चाहिए और संरचना डी केवल 17 बाइट्स है, इसलिए कंपाइलर 8 बाइट सीमा पर गठबंधन करने के लिए संरचना में 7 पैडिंग बाइट जोड़ता है।

तो आकार अब हो जाता है:

Size of D = Size of elements of D + Padding bytes for byte alignment = 17 + 7 = 24 bytes. 
+2

'दृश्य स्टूडियो में, डिफ़ॉल्ट व्यवहार यह है कि सभी structs 8 बाइट सीमा के साथ गठबंधन होते हैं। i.e.even यदि आप स्ट्रक्चर ए {char c; } और फिर आकार (ए) की जांच करें, आप देखेंगे कि यह 8 बाइट्स है। '[यह सही नहीं लगता है।] (http://rextester.com/TOJ63653) – Ayrosa

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