2010-06-15 34 views
5

मान लीजिए मैं फल का एक समूह है की ओर इशारा:सेब, संतरे, और सबसे व्युत्पन्न C++ वर्ग

class Fruit { ... }; 
class Apple : public Fruit { ... }; 
class Orange: public Fruit { ... }; 

और कुछ बहुरूपी कार्यों कि पर काम कहा फल:

void Eat(Fruit* f, Pesticide* p) { ... } 
void Eat(Apple* f, Pesticide* p) { ingest(f,p); } 
void Eat(Orange* f, Pesticide* p) { peel(f,p); ingest(f,p); } 

ठीक है, इंतजार । वहीं रुक जाओ। इस बिंदु पर ध्यान दें कि कोई भी साधु व्यक्ति फल वर्गों का वर्चुअल सदस्य फ़ंक्शन खाएगा। लेकिन यह एक विकल्प नहीं है, क्योंकि मैं एक साधु व्यक्ति नहीं हूं। इसके अलावा, मैं अपने फल वर्ग के लिए हेडर फ़ाइल में कीटनाशक * नहीं चाहता हूं।

दुःख की बात है मुझे आगे क्या करना करने में सक्षम होना चाहता हूँ वास्तव में है क्या सदस्य कार्य करता है और गतिशील बंधन अनुमति देते हैं:

typedef list<Fruit*> Fruits; 
Fruits fs; 
... 
for(Fruits::iterator i=fs.begin(), e=fs.end(); i!=e; ++i) 
    Eat(*i); 

और ज़ाहिर है, समस्या यह है कि सूचक हम पारित खाने के लिए है() हो जाएगा एक फल *, ऐप्पल * या ऑरेंज * नहीं, इसलिए कुछ भी नहीं खाया जाएगा और हम सभी बहुत भूखे होंगे।

तो क्या मैं वास्तव में इस के बजाय ऐसा करने में सक्षम होना चाहता हूँ:

Eat(MAGIC_CAST_TO_MOST_DERIVED_CLASS(*i)); 

लेकिन अपने सीमित ज्ञान के लिए, इस तरह के जादू मौजूद नहीं है, संभवतः में छोड़कर:

Eat(*i); 

यह है dynamic_cast पर कॉल से भरा एक बड़ा बुरा अगर कथन का रूप।

तो क्या कुछ रन-टाइम जादू है जिसके बारे में मुझे पता नहीं है? या क्या मुझे गतिशील_casts से भरा एक बड़ा बुरा अगर लागू कथन लागू करना चाहिए? या मुझे इसे चूसना चाहिए, इस बारे में सोचना छोड़ दें कि मैं रूबी में इसे कैसे कार्यान्वित करूंगा, और थोड़ी कीटनाशक को अपने फल हेडर में अपना रास्ता बनाने की अनुमति दूंगा?

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

class Eater 
{ 
public: 
    void Eat(Apple* f) { wash(); nom(); } 
    void Eat(Orange* f) { peel(); nom(); } 
    void Eat(Fruit* f) { nibble(); } 
}; 
... 
Eater me; 
for(Fruits::iterator i=fs.begin(), e=fs.end(); i!=e; ++i) 
    me.Eat(*i); //me tarzan! me eat! 

लेकिन फिर, यह नहीं करता है काम नहीं करता है, और सी ++ में सीधा समाधान गतिशील_कास्ट को कॉल का एक समूह लगता है।

हालांकि, जैसा कि उत्तर में से एक सुझाव देता है, एक और चालाक समाधान हो सकता है। क्या होगा यदि फलों ने मटरपील() और MustWash() जैसे कार्यों के साथ खाने वालों के लिए महत्वपूर्ण गुणों का खुलासा किया? तो फिर तुम एक भी खाओ() फ़ंक्शन के साथ मिल सकता है ...

अद्यतन: डैनियल Newby बताते हैं कि आगंतुक का उपयोग कर के रूप में भी प्रस्तुत समस्या का हल ... लेकिन यह एक अर्थ शीर्षासन का एक सा की आवश्यकता है (फल :: उपयोग या फल :: beEaten?)।

जबकि मैं कई उत्तरों स्वीकार करना चाहता हूं, मुझे लगता है कि psmears का जवाब वास्तव में भविष्य के पाठकों के लिए सबसे अच्छा है। सबको धन्यवाद।

+0

शायद आपको स्वयं को सैनिटी अस्पताल में देखना चाहिए और सदस्य कार्य करना चाहिए? –

+0

जबकि सी ++ आपको आरटीटीआई के माध्यम से वास्तविक प्रकार की जानकारी दे सकता है, मुझे वास्तविक उपप्रकार में कलाकार को मजबूर करने के किसी भी तरीके से नहीं पता है। – Jherico

+3

मुझे यकीन नहीं है कि आप जो चाहते हैं वह एकाधिक प्रेषण (http://en.wikipedia.org/wiki/Multiple_dispatch) या गतिशील प्रेषण (http://en.wikipedia.org/wiki/Dynamic_dispatch) है। – Juliano

उत्तर

3

इस तरह एक सवाल आता है, यह वास्तव में को देखने के लिए क्यों आप चाहते हैं विशेष रूप से निर्णय लेने के लिए अच्छा है - उदाहरण के लिए, क्यों आप नहीं फल वर्गों कीटनाशक के बारे में जानना चाहते हैं?

मैं वहाँ यकीन है कि इस के लिए एक अच्छा कारण है हूँ - लेकिन उस कारण को व्यक्त करने के लिए अपने मन में स्पष्ट में मदद मिलेगी कि वास्तव में क्या अपने उद्देश्य हैं - और इस बार एक नया प्रकाश एक संभव कोण पर कार्यक्रम की संरचना हेतु डालता है।

उदाहरण के लिए, आप नए आभासी तरीकों को जोड़ सकते हैं "IsEdible" और "PrepareForEating"। फिर आप इन फलों के लिए इन्हें कार्यान्वित कर सकते हैं, और एक सामान्य ईट विधि को लागू कर सकते हैं जो सभी फलों के लिए काम करता है - और पिस्की कीटनाशकों को भी निगलना करता है - सभी फल वर्गों के बिना इसके बारे में कुछ भी जानते हैं। है जो

+0

आह! तो "गतिशील_कास्ट सबकुछ" और "फल खुद को खाएं" के अलावा, एक तीसरा तरीका है: फल अपने बारे में पर्याप्त जानकारी का खुलासा करता है कि ईटर यह जानने में सक्षम है कि इसके साथ क्या किया जाए, और केवल एक ही खाएं () समारोह। मुझे यह जवाब पसंद है। –

6

आपको फिर से डिजाइन करने की आवश्यकता है। अर्थात्, जो भी आप से बचने लगते हैं, वह करें (किस कारण से, कौन जानता है।)

पॉलिमॉर्फिक व्यवहार में पॉलिमॉर्फिक कार्यों की आवश्यकता होती है।इसका मतलब है virtual फ़ंक्शन। (या dynamic_cast के के अपने सीढ़ी है, जो पूरी तरह से उद्देश्य धरा ...)

// fruit.h 
class Pesticide; // you don't need a complete type 

struct Fruit 
{ 
    virtual void Eat(Pesticide*) = 0; 
}; 

// apple.h 
class Apple : public Fruit 
{ 
    void Eat(Pesticide* p) { ... } 
}; 

// orange.h 
class Orange : public Fruit 
{ 
    void Eat(Pesticide* p) { ... } 
}; 

आप अभी भी एक नि: शुल्क समारोह * चाहते हैं:

void Eat(Fruit* f, Pesticide* p) { f->Eat(p); } 

* नोट अपनी पोस्ट है कि पहले से ही खराब डिजाइन का संकेत; अर्थात् पहले Eat समारोह:

void Eat(Fruit* f, Pesticide* p) { } 

जब एक फल के लिए कुछ नहीं कर रहे है फल को खाने के लिए समानता? एक शुद्ध वर्चुअल फ़ंक्शन एक बेहतर इंटरफ़ेस विकल्प है।

+0

एह, मुझे लगता है कि यह सिर्फ बुरे उदाहरण का संकेत है ... यहां, मैं कुछ जादुई अंडाकारों में डाल दूंगा। –

0

हेडर में मनमाने ढंग से कक्षा पॉइंटर्स रखने में कुछ भी गलत नहीं है। वे पीआईएमपीएल और अपारदर्शी पॉइंटर्स जैसे कई मुहावरे का आधार बनाते हैं। इसके अलावा, अगर आप एक साधु व्यक्ति नहीं हैं, तो आप मेरे जवाब को कैसे समझ सकते हैं?

गंभीरता से, इस समस्या को हल करने के लिए व्युत्पन्न कार्य और बहुरूपता मौजूद है। यदि आप प्रदान की गई भाषा का उपयोग करने से इनकार करते हैं, तो इसका उपयोग क्यों परेशान करते हैं? किसी भी समाधान के साथ आप आ सकते हैं किसी भी मामले में वर्चुअल फ़ंक्शन कॉल में अनुवाद किया जा सकता है, बस आप इसे कंपाइलर करने के बजाए इसे मैन्युअल रूप से कोड किया होगा।

0

क्या आप के लिए पूछ रहे हैं नहीं है यही कारण है कि आप अपने खुद के सिर में उदाहरण स्पष्ट करने के लिए :-) होगा -

बेशक

, आपका सटीक उद्देश्य पर निर्भर करता है, कि पूरी तरह से अनुपयुक्त हो सकती है मुमकिन। फ़ंक्शन ओवरलोडिंग रिज़ॉल्यूशन को संकलन समय पर पैरामीटर है जो क्लास है, इसलिए यह सही Eat फ़ंक्शन को कॉल कर सकता है। वर्चुअल सदस्य फ़ंक्शंस के लिए एकमात्र अपवाद है, जिसे आपने पहले ही अस्वीकार कर दिया है।

3

बस आईएम स्टैंडिंग राइट का उपयोग करें! पैटर्न। यह आगंतुक पैटर्न की तरह है लेकिन एक कंटेनर के बिना।

// fruit.h 
class Fruit; 
class Apple; 
class Orange; 

class Fruit_user { 
    public: 
     Fruit_user(); 
     virtual ~Fruit_user(); 
     virtual use(Apple *f) = 0; 
     virtual use(Orange *f) = 0; 
}; 

class Fruit { 
    public: 
     // Somebody with strong template fu could probably do 
     // it all here. 
     virtual void use(Fruit_user *fu) = 0; 
}; 

class Apple : public Fruit { 
    public: 
     virtual void use(Fruit_user *fu) { 
      fu->use(this); 
     } 
}; 

class Orange: public Fruit { 
    public: 
     virtual void use(Fruit_user *fu) { 
      fu->use(this); 
     } 
}; 


// dow-chemical.h 
class Pesticide_fruit_user : public Fruit_user { 
    public: 
     Pesticide_fruit_user(Pesticide *p) { 
      p_ = p; 
     } 

     virtual void use(Apple *f) { ingest(f, p_); } 
     virtual void use(Orange *f) { peel(f, p_); ingest(f, p_); } 

    private: 
     Pesticide *p_; 
}; 
+0

एक और अच्छा जवाब जो मूल पोस्ट में प्रस्तुत समस्या के लिए एक और नया समाधान प्रदान करता है। "मैं यहां खड़ा हूं" (विज़िटर की तरह) का उपयोग करके डबल-डिस्पैच प्रदान करता है और अनुरोध के अनुसार, कीटनाशक हेडर से बाहर हो जाता है। –

+0

मुझे डम्बर मिलना चाहिए ... लेकिन मुझे यहां एक बहुत ही आम 'विज़िटर' दिखाई देता है। http://en.wikipedia.org/wiki/Visitor_pattern इतना मौलिक अंतर क्या है? –

+0

एचएम। मैं सोच रहा था कि डैनियल का मतलब है कि यह अलग है क्योंकि आपके पास कभी भी एक तरह का आगंतुक है ... लेकिन अगर यह सही है, तो मुझे लगता है कि यह विज़िटर का उपयोग करने का एक विशेष तरीका है। –

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