डुप्लिकेट खाली आधार के लिए भंडारण क्यों नहीं करता है एक वाइबेटर पॉइंटर के साथ ओवरलैप?


11

इस उदाहरण पर विचार करें:

#include <iostream>

int main()
{
    struct A {};
    struct B : A {};
    struct C : A, B {};

    std::cout << sizeof(A) << '\n'; // 1
    std::cout << sizeof(B) << '\n'; // 1
    std::cout << sizeof(C) << '\n'; // 2, because of a duplicate base

    struct E : A {virtual ~E() {}};
    struct F : A, B {virtual ~F() {}};

    std::cout << sizeof(E) << '\n'; // 8, the base overlaps the vtable pointer
    std::cout << sizeof(F) << '\n'; // 16, but why?
}

(रन गॉडबोल्ट पर)

यहां आप देख सकते हैं कि struct Eखाली बेस क्लास के लिए (जो कि 1 बाइट बड़ी है) अपेक्षा के अनुरूप वाइबेटर पॉइंटर के समान स्टोरेज का उपयोग करता है।

लेकिन struct Fजिसके लिए डुप्लिकेट खाली आधार है, ऐसा नहीं होता है। इसका क्या कारण है?

मुझे GCC, Clang और MSVC पर समान परिणाम मिलते हैं। उपरोक्त परिणाम x64 के लिए हैं, इसलिए sizeof(void *) == 8


दिलचस्प बात यह है कि struct G : A, B {void *ptr;};GCC और Clang के लिए EBO (आकार 8 है) प्रदर्शन करते हैं, लेकिन MSVC नहीं है (आकार 16 है)।


3
अजीब तरह से, C(जो विरासत में मिला है ) से विरासत में मिला A, Bआपको विरासत में मिला फॉर्म Aऔर Bसीधे
मिलते

1
मैंने इस पर शोध करने का आनंद लिया है। सवाल और लिंक के लिए धन्यवाद। मुझे यकीन नहीं है कि मेरे पास एक जवाब है और इसलिए मैं केवल टिप्पणी करूंगा। क्या ऐसा हो सकता है कि यह व्युत्पत्ति से उत्पन्न अस्पष्टता से उत्पन्न होती है Cऔर F? आखिरकार, 2 * sizeof(void*) == 16जैसा आपने कहा है x86_64 पर। कंपाइलर पूरी तरह से अनुकूलन नहीं कर सकता (जैसा कि स्टोरी टेलर ने कहा) और ऐसा नहीं है।
एंड्रयू फलांगा

2
यह सामान्य है कि आपको gcc और clang पर समान परिणाम मिलते हैं, क्योंकि वे दोनों इटेनियम आयन का अनुसरण करते हैं। और अगर ऐसा है तो मुझे लगता है, जब एबीआई को परिभाषित करते हुए, वे डरते थे कि लेआउट एल्गोरिथ्म बहुत महंगा हो सकता है, इसलिए उन्होंने कुछ शॉर्टकट (उर्फ निराशा) ले लिए।
मार्क ग्लिससे

2
@RianQuinn एक डुप्लिकेट आधार संरचना को अमान्य नहीं बनाता है।
होलीब्लैककट

1
@RianQuinn विभिन्न "पथों" के माध्यम से एक ही वर्ग से कई बार विरासत में मिली C ++ में पूरी तरह से मान्य है। यदि आप एक हीरे की संरचना बनाना चाहते हैं, अर्थात आधार वर्ग केवल एक बार है, तो आपको आभासी विरासत का उपयोग करना होगा। लेकिन अगर आप हीरा नहीं चाहते हैं और डुप्लिकेट बेस क्लास आपके लिए कोई समस्या नहीं है, तो यह भाषा के लिए भी कोई मुद्दा नहीं है। ओपी का कोड केवल एक चेतावनी उत्पन्न करता है, जिसमें कहा गया है कि दूसरा A, जिसके माध्यम से विरासत में मिला है B, उसे एक्सेस नहीं किया जा सकता है। यह ठीक है। केवल अगर आप वास्तव में इसे एक्सेस करने की कोशिश करते हैं, जैसा कि आपके उदाहरण में, आपको एक त्रुटि मिलती है।
16

जवाबों:


4

क्योंकि कंपाइलर A संरचना के बाद एक बाइट गद्दी जोड़ता है

F {vptr (8) + 0 सदस्य A + 1 पैडिंग से (क्योंकि A खाली है) +0 से b} = 9 तब कंपाइलर 7 बाइट्स पैडिंग को जोड़कर स्ट्रक्चर के स्टोरेज को अलाइन करता है;

E {vptr (8) + 0 सदस्यों के लिए A} = 8 कोई पैडिंग आवश्यक नहीं है

Microsoft से

हर डेटा ऑब्जेक्ट की एक संरेखण-आवश्यकता होती है। संरचनाओं के लिए, आवश्यकता इसके सदस्यों में सबसे बड़ी है। प्रत्येक ऑब्जेक्ट को एक ऑफ़सेट आवंटित किया जाता है ताकि% संरेखण-आवश्यकता == 0 ऑफसेट हो

https://docs.microsoft.com/en-us/cpp/c-language/storage-and-alignment-of-structures?view=vs-2019

संपादित करें:

यहाँ मेरा डेमो है:

int main()
{
    C c;
    A* a = &c;
    B* b = &c;

    std::cout << sizeof(A) << " " << a << '\n'; 
    std::cout << sizeof(B) << " " << b << '\n'; 
    std::cout << sizeof(C) << " " << &c << '\n'; 

    E e;
    a = &e;
    std::cout << sizeof(E) <<" " << &e << " " << a << '\n'; 

    F f;
    a = &f;
    b = &f;
    std::cout << sizeof(F) << " " << &f << " " << a << " " << b << '\n';

}

उत्पादन:

1 0000007A45B7FBB4
1 0000007A45B7FBB5
1 0000007A45B7FBB4
8 0000007A45B7FC18 0000007A45B7FC20
16 0000007A45B7FC38 0000007A45B7FC40 0000007A45B7FC41

जैसा कि आप देख सकते हैं कि एक और बी कभी भी एक दूसरे के साथ ओवरलैप नहीं होते हैं और कई उत्तराधिकार में vptr के साथ प्रत्येक का अपना सूचक मूल्य होता है

VC2019 x64 बिल्ड द्वारा संकलित नोट


मुझे नहीं लगता कि यह कैसे काम करता है। हालांकि Aकोई सदस्य नहीं है, फिर भी यह 1 बाइट पर कब्जा कर लेता है (जिसे किसी अन्य वस्तु के साथ साझा किया जा सकता है)। में E, Avptr के बाद स्थित नहीं है; यह vptr की पहली बाइट को ओवरलैप करता है। (यहां एक डेमो है ; मैंने Aसुलभ बनाने के लिए कोड को थोड़ा संशोधित किया है।) उसी में पहली बार होता Aहै F। चूंकि Aऔर B) को vptr के शीर्ष पर रखा जा सकता है, मुझे यकीन नहीं है कि ऐसा क्यों नहीं होता है B
होलीब्लैककैट 22

@ HolyBlackCat लेकिन यह वही हुआ जो चेक टेस्ट कोड था
अहमद एनटर

अहा, इसलिए MSVC यहाँ GCC / Clang से अलग व्यवहार करता है; यह Avptr के शीर्ष पर रखने के लिए पर्याप्त स्मार्ट नहीं है । यह समझा सकता है कि MSVC पर आउटपुट 16 क्यों है, लेकिन मुझे यकीन नहीं है कि जीसीसी और क्लैंग के साथ क्या हो रहा है।
होलीब्लैककैट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.