c++ - गद्दी और वंशानुक्रम के संबंध में वर्ग और संरचना के बीच अंतर




c++11 gcc (2)

नीचे के सभी GCC 9.1 पर संकलक एक्सप्लोरर का उपयोग करते हुए, x86-64 में, -O3 का उपयोग करके -O3

मेरे पास यह कोड है:

struct Base {
    Base() {}
    double foo;
    int bar;
};

struct Derived : public Base {
    int baz;
};

int main(int argc, char** argv)
{
    return sizeof(Derived);
}

https://godbolt.org/z/OjSCZB

यह सही ढंग से 16 रिटर्न करता है, जैसा कि मैं उम्मीद करूंगा, foo लिए 8 बाइट्स, और bar लिए 4 बाइट्स और baz लिए 4 बाइट्स। यह केवल इसलिए काम करता है क्योंकि Derived Base से विरासत में मिला है और इसलिए Derived को Base और Derived दोनों तत्वों से युक्त एक ही प्रकार होने के कारण bar के बाद पैड नहीं करना पड़ता है।

मेरे पास दो प्रश्न हैं, जैसा कि नीचे है:

पहला प्रश्न

यदि मैं Base() {} के स्पष्ट कंस्ट्रक्टर को हटाता हूं, तो यह 16 बजाय 24 वापस लौटने लगता है। यानी यह bar और baz बाद पैडिंग जोड़ता है।

https://godbolt.org/z/0gaN5h

मैं स्पष्ट नहीं कर सकता कि स्पष्ट डिफॉल्ट कंस्ट्रक्टर होने का कोई निहित डिफॉल्ट कंस्ट्रक्टर होने के कारण अलग है।

दूसरा सवाल

अगर मैं Base लिए struct को बदलूं, तो यह वापस 16 बदल जाता है। मैं यह भी नहीं समझा सकता। एक्सेस संशोधक संरचना के आकार को क्यों बदलेंगे?

https://godbolt.org/z/SCYKwL


अपने बेस क्लास के साथ आपको टेल पैडिंग के 4 बाइट्स मिलेंगे, और डेरेव्ड क्लास के साथ भी, यही कारण है कि Derived के आकार के लिए यह सामान्य रूप से 24 bytes होना चाहिए।

यह 16 बाइट्स बन जाता है, क्योंकि आपका कंपाइलर टेल पैडिंग का पुन: उपयोग करने में सक्षम है।

हालाँकि टेल पैडिंग का पुन: उपयोग POD प्रकार (सभी सदस्यों को सार्वजनिक, डिफ़ॉल्ट रूप से निर्माणकर्ता, आदि ...) के साथ समस्याग्रस्त है, क्योंकि यह आम धारणा को तोड़ता है जो एक प्रोग्रामर बनाता है। (इसलिए मूल रूप से कोई भी समझदार संकलक फली प्रकारों के लिए टेल पैडिंग पुन: उपयोग नहीं करेगा)

आइए दिखाते हैं कि कंपाइलर POD प्रकारों के लिए tail padding reuse उपयोग का उपयोग करेंगे:

struct Base {
    double foo;
    int bar;
};

struct Derived : Base {
    int baz;
};

int main(int argc, char** argv)
{
    // if your compiler would reuse the tail padding then the sizes would be:
    // sizeof(Base) == 16
    // sizeof(Derived) == 16

    Derived d;
    d.baz = 12;
    // trying to zero *only* the members of the base class,
    // but this would zero also baz from derived, not very intuitive
    memset((Base*)&d, 0, sizeof(Base));

    printf("%d", d.baz); // d.baz would now be 0!
}

बेस क्लास में एक स्पष्ट कंस्ट्रक्टर जोड़ते समय, या struct कीवर्ड को class बदलकर, Derived क्लास अब POD परिभाषा को संतुष्ट नहीं करता है और इसलिए टेल पैडिंग का पुन: उपयोग नहीं होता है।


यह सब इस बात पर उबलता है कि आपका प्रकार एक समुच्चय है या नहीं। साथ में

struct Base {
    Base() {}
    double foo;
    int bar;
};

struct Derived : public Base {
    int baz;
};

कंस्ट्रक्टर की वजह से Base एग्रीगेट नहीं है। जब आप कंस्ट्रक्टर को हटाते हैं, तो आप Base एक एग्रीगेट बनाते हैं, जो .com/q/47914612/560648 , जिसका अर्थ है कि स्पेस के लिए gcc "ऑप्टिमाइज़ नहीं करेगा" और व्युत्पन्न ऑब्जेक्ट बेस का उपयोग नहीं करेगा पूंछ की गद्दी।

जब आप कोड को बदलेंगे

class Base {
    double foo;
    int bar;
};

struct Derived : public Base {
    int baz;
};

foo और bar अब निजी हैं (becauses classes में निजी एक्सेसिबिलिटी बाय डिफॉल्ट होती है) जिसका मतलब है कि Base अब एक एग्रीगेट नहीं है क्योंकि कुल सदस्यों को निजी सदस्य रखने की अनुमति नहीं है। इसका मतलब है कि हम वापस आ गए हैं कि पहला मामला कैसे काम करता है।







gcc