2008-09-24 11 views
23

वहाँ ऐसा करने में एक पक्ष प्रभाव है:क्या सी ++ में सी संरचना को उपclass करना संभव है और सी कोड में संरचना के लिए पॉइंटर्स का उपयोग करना संभव है?

सी कोड:

struct foo { 
     int k; 
}; 

int ret_foo(const struct foo* f){ 
    return f.k; 
} 

सी ++ कोड:

class bar : public foo { 

    int my_bar() { 
     return ret_foo((foo)this); 
    } 

}; 

वहाँ सी ++ कोड के चारों ओर एक extern "C" है और प्रत्येक कोड का अपना संकलन के अंदर है इकाई।

क्या यह पोर्टेबल कंपेलरों में है?

+0

मैं तुम्हारा मतलब अनुमान है कि इस, या और भी बेहतर: static_cast (यह) –

+0

मैं एक विधि के साथ एक निर्वासन 'सी' के प्रभाव को एक वर्ग के आसपास के बारे में उत्सुक हूँ ... फिर भी, कारण आप ऐसा करना चाहते हैं हमें जानकारी या वैकल्पिक समाधान की पेशकश करने में मदद मिलेगी। – paercebal

+0

जब आप "कंप्यूटर्स में पोर्टेबल" कहते हैं, तो क्या आप एक कोडर के साथ सी कोड को संकलित करते हैं, और सी ++ दूसरे के साथ जोड़ते हैं, और दोनों को एक साथ जोड़ते हैं? – Roddy

उत्तर

26

यह पूरी तरह से कानूनी है। सी ++ में, कक्षाएं और structs समान अवधारणाएं हैं, अपवाद के साथ कि सभी संरचना सदस्य डिफ़ॉल्ट रूप से सार्वजनिक होते हैं। यही एकमात्र अंतर है। तो पूछना कि क्या आप एक संरचना का विस्तार कर सकते हैं यह पूछने से अलग नहीं है कि आप कक्षा का विस्तार कर सकते हैं या नहीं।

यहां एक चेतावनी है। संकलक से कंपाइलर तक लेआउट स्थिरता के की कोई गारंटी नहीं है। इसलिए यदि आप अपने सी कोड को अपने सी ++ कोड से अलग कंपाइलर के साथ संकलित करते हैं, तो आप सदस्य लेआउट से संबंधित समस्याओं (विशेष रूप से पैडिंग) में भाग सकते हैं। यह वही विक्रेता से सी और सी ++ कंपाइलर्स का उपयोग करते समय भी हो सकता है।

I यह जीसीसी और जी ++ के साथ हुआ था। मैंने एक परियोजना पर काम किया जो कई बड़े structs का इस्तेमाल किया। दुर्भाग्यवश, जी ++ ने जीसीसी की तुलना में ढांचे को काफी हद तक पैक किया, जिससे सी और सी ++ कोड के बीच वस्तुओं को साझा करने में महत्वपूर्ण समस्याएं आईं। हमें अंततः पैकिंग सेट करना पड़ा और सी और सी ++ कोड को स्ट्रक्चर का इलाज करने के लिए पैडिंग डालना पड़ा। ध्यान दें, हालांकि, यह समस्या subclassing के बावजूद हो सकती है। वास्तव में हम इस मामले में सी संरचना subclassing नहीं थे। (Foo *)

+1

असल में, structs और कक्षाओं के बीच एक और अंतर है: आधार वर्गों की डिफ़ॉल्ट दृश्यता structs में सार्वजनिक है, लेकिन कक्षाओं में निजी है। – Aconcagua

2

वाह, यह बुरा है।

क्या यह पोर्टेबल कंपेलरों में है?

सबसे निश्चित रूप से नहीं। निम्नलिखित पर विचार करें:

foo* x = new bar(); 
delete x; 
इस काम करने के लिए

लिए, foo नाशक आभासी होना चाहिए जो यह स्पष्ट रूप से नहीं है। जब तक आप new का उपयोग नहीं करते हैं और जब तक व्युत्पन्न ऑब्जेक्ट में कस्टम विनाशक नहीं होते हैं, तब भी, आप भाग्यशाली हो सकते हैं।

/संपादित करें: दूसरी तरफ, यदि कोड केवल प्रश्न के रूप में उपयोग किया जाता है, तो विरासत को संरचना पर कोई फायदा नहीं होता है। बस m_pGladiator द्वारा दी गई सलाह का पालन करें।

+0

लेकिन यह वह कोड नहीं है जिसे मैं लिखना चाहता हूं। बार को फ़ंक्शन कॉल के लिए केवल foo पर डाला जा सकता है, और foo पर संचालित सभी सी फ़ंक्शंस कॉन्स हैं। (जैसा कि उदाहरण में है।) –

+0

हां, उस स्थिति में कोड थोड़े ठीक है। यह सिर्फ दुरुपयोग के लिए begs। ;-) –

+0

ऐसा लगता है कि foo * इंटरफ़ेस केवल सी द्वारा उपयोग किया जाता है। सी कोड वैसे भी इसे हटाने में सक्षम नहीं होगा: [1] यह मालिक नहीं है और [2] यह सी ++ नहीं है। – MSalters

9

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

foo * m_pfoo;

बार कक्षा में और यह वही काम करेगा।

अन्य चीज जो आप कर सकते हैं वह एक और वर्ग फूवापर बनाने के लिए है, जिसमें संबंधित गेटर विधि के साथ स्वयं संरचना शामिल है। फिर आप रैपर को उपclass कर सकते हैं। इस तरह आभासी विनाशक के साथ समस्या चली गई है।

+0

यह सबसे अच्छा जवाब है। स्वीकृत उत्तर के विपरीत वहां कुछ विकल्प बताए गए हैं जो आपको vtables और संरचना लेआउट के साथ समस्याओं से बचने में मदद करते हैं। – Alex

1

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

विनाशकों के साथ समस्या असली है, लेकिन किसी भी समय बेस क्लास विनाशक केवल सी structs के लिए वर्चुअल नहीं है लागू होता है। यह ऐसा कुछ है जिसे आपको जागरूक करने की आवश्यकता है लेकिन इस पैटर्न का उपयोग करने से रोक नहीं है।

+0

व्यवहार अच्छी तरह परिभाषित नहीं है - कम से कम मेरे पढ़ने के लिए std। क्या आपके पास पाठ का संदर्भ है जो कहता है कि यह कानूनी है? –

+0

कोई संदर्भ नहीं ... लेकिन मैं नीचे दिए गए उद्धरण को पूरी तरह से ग्रोक नहीं करता हूं। इससे कोई उपयोगी डाउन-कास्टिंग नहीं रोका जा सकता है, मुझे नहीं लगता कि बेस क्लास के मामले में सी संरचना –

+0

दुर्भाग्य से, मैं एक मानक संदर्भ नहीं ढूंढ पा रहा हूं। लेकिन यह मुझे एक आईएसओ सी ++ कमेटी सदस्य द्वारा इंगित किया गया था कि एक वस्तु केवल पीओडी है यदि यह पीओडी प्रकार का ठोस उदाहरण है। अर्थात। पीओडी बेस उप ऑब्जेक्ट जैसी कोई चीज नहीं है। मैं नीचे दिए गए मेरे उत्तर में अधिक जानकारी जोड़ूंगा। –

1

यह काम करेगा, और पोर्टेबल लेकिन आप किसी वर्चुअल फ़ंक्शंस (जिसमें विनाशक शामिल हैं) का उपयोग नहीं कर सकते हैं।

मैं अनुशंसा करता हूं कि ऐसा करने के बजाय आपके पास बार में एक फू है।

class Bar 
{ 
private: 
    Foo mFoo; 
}; 
0

मुझे नहीं पता कि आप एक सदस्य विधि को ret_foo क्यों नहीं बनाते हैं। आपका वर्तमान तरीका आपके कोड को समझने में बहुत मुश्किल बनाता है। एक सदस्य वर्ग के साथ पहली जगह में वास्तविक कक्षा का उपयोग करने और प्राप्त/सेट विधियों के बारे में इतना मुश्किल क्या है?

मुझे पता है कि सी ++ में structs subclass करना संभव है, लेकिन खतरे यह है कि दूसरों को यह समझने में सक्षम नहीं होगा कि आपने जो कोड किया है, क्योंकि यह शायद ही कभी ऐसा करता है कि कोई वास्तव में ऐसा करता है। मैं इसके बजाय एक मजबूत और सामान्य समाधान के लिए जाना होगा।

+0

बस इसलिए क्योंकि ret_foo शुद्ध सी में लिखा गया है, जहां मेरे पास सदस्य विधियां नहीं हैं। इसके अलावा, मेरे पास ret_foo के लिए कोड नहीं है, यह मेरे लिए प्रदान की गई एक एपीआई है। –

+0

उन चीजों को उप-वर्ग न करें जिन पर आपका नियंत्रण नहीं है। इसे एक सदस्य चर बनाओ और इसे वहां संभाल लें। उपयोगी होने पर इसे किसी ऑब्जेक्ट में लपेटें। व्युत्पन्न की तुलना में संरचना यहां बेहतर है। – Thorsten79

0

यह शायद काम करेगा लेकिन मुझे विश्वास नहीं है कि इसकी गारंटी है। निम्नलिखित आईएसओ सी ++ 10/5:

एक बेस क्लास सबोबजेक्ट में एक ही प्रकार की सबसे व्युत्पन्न वस्तु के लेआउट से एक लेआउट (3.7) अलग हो सकता है।

यह देखना मुश्किल है कि "वास्तविक दुनिया" में वास्तव में यह मामला कैसे हो सकता है।

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

लब्बोलुआब यह है कि मानक स्थानों पर जहां एक आधार वर्ग subobject लेआउट है कि एक ही बेस प्रकार के साथ एक ठोस वस्तु से अलग हो सकता है की संख्या सीमित नहीं किया है। नतीजा यह है कि आपके पास हो सकता है कि पीओडी-नेस इत्यादि जैसी कोई धारणा बेस क्लास सबोबजेक्ट के लिए जरूरी नहीं है।

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

एक वैकल्पिक दृष्टिकोण और जिसका व्यवहार अच्छी तरह से परिभाषित किया गया है है 'foo' 'बार' के एक सदस्य बनाने के लिए और एक रूपांतरण ऑपरेटर जहां यह आवश्यक है प्रदान करते हैं।

class bar { 
public:  
    int my_bar() { 
     return ret_foo(foo_); 
    } 

    // 
    // This allows a 'bar' to be used where a 'foo' is expected 
    inline operator foo&() { 
    return foo_; 
    } 

private:  
    foo foo_; 
}; 
+0

मुझे संदेह है कि यह आभासी विरासत के कारण होगा। – bk1e

+0

समस्या यह है कि, मुझे विश्वास नहीं है कि मानक वास्तव में उस स्थान को सीमित करता है जो यह हो सकता है। –

+0

रिचर्ड, यह उद्धरण इस बात पर ध्यान दिए बिना कि आप structs या कक्षाओं, या कुछ फंकी मिश्रण का उपयोग कर रहे हैं या नहीं। जो कुछ भी कहता है वह यह है कि संकलक वस्तु के अंतर्निहित लेआउट को बदल सकता है कि यह कैसे फिट बैठता है। यह कुछ तोड़ नहीं है। –

3

"कभी ठोस वर्ग से निकाले जाते हैं।" - सूटर

"गैर पत्ती कक्षाएं सार बनाओ।" - मेयर्स

यह बस गैर इंटरफ़ेस कक्षाएं उपवर्ग के लिए गलत है। आपको अपने पुस्तकालयों को दोबारा करना चाहिए।

तकनीकी रूप से, आप जो भी चाहते हैं वह कर सकते हैं, जब तक आप अपरिभाषित व्यवहार का आह्वान नहीं करते हैं, ई। जी।, व्युत्पन्न वर्ग में पॉइंटर को अपने बेस क्लास सबोबजेक्ट में पॉइंटर से हटा रहा है। आपको C++ कोड के लिए extern "C" की भी आवश्यकता नहीं है। हाँ, यह पोर्टेबल है। लेकिन यह खराब डिजाइन है।

+4

मैं मेयर और सटर द्वारा इस तरह के निश्चित बयान के साथ बिल्कुल सहमत नहीं हूं। कंक्रीट कक्षाओं से विरासत में आप बहुत कुछ कर सकते हैं, खासकर यदि आप केवल व्युत्पन्न कक्षाओं में विधियों को जोड़ते हैं, न कि डेटा सदस्यों। इस तरह आपको टुकड़ा करने की समस्या नहीं है। – QBziZ

+2

मुझे लगता है कि आप बिंदु खो रहे हैं। मेरा अनुमान है कि ओपी में मौजूदा सी मॉड्यूल हैं और कक्षाओं में कुछ ढांचे को 'लपेटने' की इच्छा है, जबकि अभी भी विरासत प्रणाली के लिए सी इंटरफ़ेस की पेशकश की जा रही है। यदि वह structs के लिए सी के साथ फंस गया है, तो सटर और मेयर बिल्कुल मदद नहीं कर रहे हैं। – Roddy

3

यह पूरी तरह से कानूनी है, हालांकि यह अन्य प्रोग्रामर के लिए भ्रमित हो सकता है।

आप विधियों और रचनाकारों के साथ सी-structs का विस्तार करने के लिए विरासत का उपयोग कर सकते हैं।

नमूना:

struct POINT { int x, y; } 
class CPoint : POINT 
{ 
public: 
    CPoint(int x_, int y_) { x = x_; y = y_; } 

    const CPoint& operator+=(const POINT& op2) 
    { x += op2.x; y += op2.y; return *this; } 

    // etc. 
}; 

विस्तार structs हो सकता है "और" बुराई है, लेकिन कुछ तुम क्या करने के लिए मना कर रहे हैं नहीं है।

+0

मुझे इसके बारे में इतना यकीन नहीं है। मेरा मानना ​​है कि (कम से कम मानक के वर्तमान संस्करण में) कि क्लास क्लास सबोबजेक्ट के रूप में कक्षा का लेआउट अलग-अलग हो सकता है यदि आपके पास उस वर्ग का उदाहरण है। (आईएसओ सी ++ 10/5 के तहत नोट देखें) –

+1

यदि व्युत्पन्न कक्षा कोई डेटा नहीं जोड़ती है, और कोई वर्चुअल विधियां नहीं जोड़ती है, तो कुछ भी बुरा नहीं होना चाहिए। अब, यह शायद डिज़ाइन में बुरा है, लेकिन यदि आवश्यकता एक डिफ़ॉल्ट कन्स्ट्रक्टर और कुछ सहायक कार्यों को जोड़ना है, तो ... अच्छा ... यह डिज़ाइन नहीं है। यह सिर्फ एक लापता विशेषता को ठीक कर रहा है, और कुछ नहीं। – paercebal

2

यह पूरी तरह से कानूनी है, और आप इसे एमएफसी सीआरईटी और सीपीओटी कक्षाओं के साथ अभ्यास में देख सकते हैं। सीपीओटी POINT (windef.h में परिभाषित) से निकला है, और सीआरईटी आरईसीटी से निकला है। आप बस सदस्य कार्यों के साथ एक वस्तु सजा रहे हैं। जब तक आप अधिक डेटा के साथ ऑब्जेक्ट का विस्तार नहीं करते हैं, तो आप ठीक हैं। असल में, यदि आपके पास एक जटिल सी स्ट्रक्चर है जो डिफ़ॉल्ट रूप से दर्द होता है- प्रारंभ करें, इसे उस श्रेणी के साथ विस्तारित करें जिसमें डिफ़ॉल्ट कन्स्ट्रक्टर होता है, उस समस्या से निपटने का एक आसान तरीका है।

आप ऐसा करते हैं यहां तक ​​कि अगर:

foo *pFoo = new bar; 
delete pFoo; 

तो आप ठीक हो के बाद से अपने निर्माता और नाशक तुच्छ हैं, और आप किसी भी अतिरिक्त स्मृति आवंटित नहीं किया है।

आपको अपने सी ++ ऑब्जेक्ट को 'बाहरी "सी' 'के साथ लपेटने की भी आवश्यकता नहीं है, क्योंकि आप वास्तव में सी ++ टाइप को सी फ़ंक्शंस में नहीं पारित कर रहे हैं।

+0

आप गलत हैं। बेस क्लास में पॉइंटर द्वारा व्युत्पन्न कक्षा में पॉइंटर को हटाना अपरिभाषित व्यवहार है। अवधि। –

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