2012-06-15 11 views
7

मुझे पता है कि एकाधिक विरासत के मेमोरी लेआउट को परिभाषित नहीं किया गया है, इसलिए मुझे इस पर भरोसा नहीं करना चाहिए। हालांकि, क्या मैं इसे एक विशेष मामले में भरोसा कर सकता हूं। यही है, एक वर्ग में केवल एक "असली" सुपर क्लास है। अन्य सभी "खाली वर्ग" हैं, यानी, कक्षाएं जिनमें न तो फ़ील्ड और न ही वर्चुअल विधियां हैं (यानी उनके पास केवल गैर-वर्चुअल विधियां हैं)। इस मामले में, इन अतिरिक्त वर्गों को कक्षा के स्मृति लेआउट में कुछ भी नहीं जोड़ना चाहिए। (अधिक संक्षेप में, सी ++ 11 शब्द में, कक्षा में मानक-लेआउट)सी ++ "एकाधिक कक्षाओं" के साथ एकाधिक विरासत मेमोरी लेआउट

क्या मैं अनुमान लगा सकता हूं कि सभी सुपरक्लासों का कोई ऑफ़सेट नहीं होगा? उदा .:

#include <iostream> 

class X{ 

    int a; 
    int b; 
}; 

class I{}; 

class J{}; 

class Y : public I, public X, public J{}; 

int main(){ 

    Y* y = new Y(); 
    X* x = y; 
    I* i = y; 
    J* j = y; 

    std::cout << sizeof(Y) << std::endl 
        << y << std::endl 
        << x << std::endl 
        << i << std::endl 
        << j << std::endl; 
} 

यहाँ, YX केवल वास्तविक आधार वर्ग होने के साथ वर्ग है। कार्यक्रम (जब ++ जी के साथ 4.6 लिनक्स पर संकलित) के उत्पादन में इस प्रकार है:

0x233f010

0x233f010

0x233f010

0x233f010

जैसा कि मैंने निष्कर्ष निकाला, कोई पोई नहीं है नटर समायोजन। लेकिन क्या यह कार्यान्वयन विशिष्ट है या क्या मैं इस पर भरोसा कर सकता हूं। यानी, अगर मुझे I (और मुझे पता है कि केवल ये वर्ग मौजूद हैं) का ऑब्जेक्ट प्राप्त होता है, तो क्या मैं इसे पर डालने के लिए reinterpret_cast का उपयोग कर सकता हूं?

मेरी आशा है कि मैं इस पर भरोसा कर सकता हूं क्योंकि कल्पना कहती है कि किसी ऑब्जेक्ट का आकार कम से कम बाइट होना चाहिए। इसलिए, संकलक एक और लेआउट नहीं चुन सकता है। यदि X के सदस्यों के पीछे I और J लेआउट होगा, तो उनका आकार शून्य होगा (क्योंकि उनके पास कोई सदस्य नहीं है)। इसलिए, ऑफसेट के बिना सभी सुपर क्लास को संरेखित करना एकमात्र उचित विकल्प है।

क्या मैं सही हूं या मैं आग से खेल रहा हूं अगर मैं I से X पर reinterpret_cast का उपयोग करता हूं?

+3

विरासत, एकल या एकाधिक, खाली अड्डों का स्मृति लेआउट या नहीं, परिभाषित नहीं किया गया है। –

+1

मैं कहूंगा कि आप आग से खेल रहे हैं, क्योंकि आप एक बहुत ही अजीब निर्माण करने की कोशिश कर रहे हैं जो निश्चित रूप से भविष्य में इसे समझने वाले किसी को भी दर्द और दुख का कारण बन जाएगा, भले ही आप अपग्रेड करते समय ब्रेक न करें आपका कंपाइलर! – Rook

+0

सौभाग्य से, आपके कथन अब C++ 11 में सही नहीं हैं, उत्तर देखें :) – gexicide

उत्तर

8

सी ++ 11 में संकलक को मानक लेआउट प्रकारों के लिए खाली बेस-श्रेणी अनुकूलन का उपयोग करने की आवश्यकता है। देख https://stackoverflow.com/a/10789707/981959

अपने विशिष्ट उदाहरण के लिए सभी प्रकार के मानक लेआउट वर्ग हैं और आम आधार वर्ग या सदस्यों (देखें नीचे) की जरूरत नहीं है तो आप में है कि व्यवहार पर भरोसा कर सकते सी ++ 11 (और व्यवहार में, मुझे लगता है कि कई कंपाइलर्स पहले से ही उस नियम का पालन करते हैं, निश्चित रूप से जी ++ ने किया है, और Itanium C++ ABI के बाद अन्य।)

एक चेतावनी: सुनिश्चित करें कि आपके पास एक ही प्रकार के किसी भी बेस क्लास नहीं हैं, क्योंकि वे अलग-अलग पते पर होना चाहिए, जैसे

struct I {}; 

struct J : I {}; 
struct K : I { }; 

struct X { int i; }; 

struct Y : J, K, X { }; 

#include <iostream> 

Y y; 

int main() 
{ 
    std::cout << &y << ' ' << &y.i << ' ' << (X*)&y << ' ' << (I*)(J*)&y << ' ' << (I*)(K*)&y << '\n'; 

} 

प्रिंट:

0x600d60 0x600d60 0x600d60 0x600d60 0x600d61 

प्रकार YI ठिकानों में से केवल एक पर शून्य की भरपाई की जा सकती है, हालांकि X उप वस्तु पर है तो शून्य ऑफसेट के लिए (अर्थातoffsetof(Y, i) शून्य है) और I अड्डों में से एक आधार एक ही पते पर है, लेकिन अन्य I आधार (कम से कम G ++ और Clang ++) ऑब्जेक्ट में एक बाइट है, इसलिए यदि आपको I* मिल गया है तो आप reinterpret_cast से नहीं कर सकते हैं क्योंकि आप जोI उप वस्तु यह की ओर इशारा किया, I पर 0 ऑफसेट पता नहीं या I पर 1.

संकलक दूसरा I उप वस्तु डाल करने के लिए कम से 1 ऑफसेट यह ठीक है ऑफसेट (यानी int) क्योंकि I में कोई गैर-स्थैतिक डेटा सदस्य नहीं हैं, इसलिए आप actuall नहीं कर सकते y dereference या उस पते पर कुछ भी एक्सेस, केवल उस पते पर ऑब्जेक्ट के लिए एक सूचक प्राप्त करें। यदि आपने I पर गैर स्थैतिक डेटा सदस्यों को जोड़ा तो Y अब मानक लेआउट नहीं होगा और ईबीओ का उपयोग नहीं करना पड़ेगा, और offsetof(Y, i) अब शून्य नहीं होगा।

+0

सही है, इसलिए यह काम करता है :) – gexicide

+0

ध्यान दें कि मैंने अपने संपादन में विस्तारित महत्वपूर्ण चेतावनी को ध्यान में रखते हुए –

+0

हां, हां। मुझे यह सुनिश्चित करना होगा कि कक्षा में मानक-लेआउट है। धन्यवाद! – gexicide