2009-08-11 16 views
27

This question कुछ घंटों पहले यहां पूछा गया था और मुझे एहसास हुआ कि मैंने वास्तव में कभी भी अपने कोड में कॉन्वेंट रिटर्न प्रकारों का उपयोग नहीं किया है। उन के लिए यह सुनिश्चित नहीं है कि कॉन्वर्सिस क्या है, यह रिटर्न प्रकार (आमतौर पर) वर्चुअल फ़ंक्शंस को अलग करने की इजाजत देता है, बशर्ते कि प्रकार एक ही विरासत पदानुक्रम का हिस्सा हों। उदाहरण के लिए:सी ++ कब्रिस्तान सबसे अच्छा समाधान कब होता है?

struct A { 
    virtual ~A(); 
    virtual A * f(); 
    ... 
}; 

struct B : public A { 
    virtual B * f(); 
    ... 
}; 

दो एफ() कार्यों के विभिन्न रिटर्न प्रकारों को कॉन्वेंट कहा जाता है।

struct B : public A { 
    virtual A * f(); 
    ... 
}; 

तो, मेरे सवाल: किसी को भी एक वास्तविक दुनिया उदाहरण है जहाँ लौट covariant आभासी कार्यों के प्रकार के लिए आवश्यक हैं है सी ++ के पुराने संस्करण, एक ही हो तो बी की तरह लग रहे करने के लिए होता वापसी प्रकार की आवश्यकता , या बस आधार सूचक या संदर्भ लौटने के लिए एक बेहतर समाधान का उत्पादन?

+0

जब मैंने प्रश्न पढ़ा तो मेरी प्रतिक्रिया बिल्कुल वही थी। समस्या क्या है - कॉन्वेंट रिटर्न प्रकारों का उपयोग न करें! –

उत्तर

27

कैननिकल उदाहरण .clone()/.copy() विधि है। तो आप हमेशा

obj = obj->copy(); 

चाहे ओबीजे का प्रकार क्या हो।

संपादित करें: यह क्लोन विधि ऑब्जेक्ट बेस क्लास में परिभाषित की जाएगी (जैसा कि यह वास्तव में जावा में है)। तो अगर क्लोन को संविधान नहीं किया गया था, तो आपको या तो रूट बेस क्लास के तरीकों तक सीमित करना होगा, या प्रतिलिपि होगी (जिसमें प्रतिलिपि के स्रोत ऑब्जेक्ट की कक्षा की तुलना में केवल बहुत कम विधियां होंगी)।

+2

लेकिन आपको (IMHO) देखभाल नहीं करना चाहिए कि ए * ए = some_a_or_b-> क्लोन(); बस काम करता है, और फिर आप क्लोन पॉइंटर पर अन्य वर्चुअल विधियों का उपयोग करते हैं। –

+0

मैं एक आवश्यक विशेषता के रूप में, सिंटैक्टिक चीनी के रूप में अधिक covariance देखते हैं। आप हमेशा ए * ए = बी-> क्लोन() कर सकते हैं; dynamic_cast (ए) -> initB(); – Christopher

+5

छोड़ दिया गया है कि covariance के साथ प्रकार स्थिर रूप से चेक किया गया है। – AProgrammer

1

एक और उदाहरण एक ठोस कारखाना है जो अमूर्त के बजाय कंक्रीट कक्षाओं में पॉइंटर्स लौटाएगा (मैंने फैक्ट्री में आंतरिक उपयोग के लिए इसका उपयोग किया है जब कारखाने को यौगिक वस्तुओं का निर्माण करना था)।

+0

संभवतः आप पॉइंटर्स को डायनामिक_कास्ट करने के लिए काम करते हैं कि वास्तव में आपको किस ठोस उदाहरण मिल गए हैं? इस मामले में आपको कॉन्वर्सिस की आवश्यकता नहीं है। या क्या मैं कुछ न कुछ भूल रहा हूं? –

+0

'dynamic_cast' के लिए आवश्यक है कि प्रकार polymorphic हैं और यह संभावित रूप से महंगा है। हालांकि, कारखाने वर्ग में फ़ंक्शन टेम्पलेट का उपयोग करके एक ही परिणाम प्राप्त किया जा सकता है। यह दो कार्य करेगा: जांचें कि प्रकार संबंधित हैं (isbaseclass या जो कुछ भी) और फिर जेनेरिक परिणाम को व्युत्पन्न प्रकार पर डालें। कोई गतिशील कलाकार आवश्यक नहीं है। –

+0

संभावित रूप से महंगे से मेरा मतलब एक स्थिर_कास्ट से अधिक महंगा है और फिर भी यह केवल तभी है जहां आप इसे लाखों बार बुला रहे हैं जिससे इससे कोई फर्क पड़ता है। ;) –

4

मुझे लगता है कि कारखाने के तरीकों की घोषणा करते समय कॉन्वर्सेंस उपयोगी हो सकता है जो एक विशिष्ट वर्ग लौटाता है, न कि इसकी बेस क्लास। This article काफी अच्छी तरह से इस परिदृश्य बताते हैं, और निम्न कोड उदाहरण में शामिल हैं:

class product 
{ 
    ... 
}; 

class factory 
{ 
public: 
    virtual product *create() const = 0; 
    ... 
}; 

class concrete_product : public product 
{ 
    ... 
}; 

class concrete_factory : public factory 
{ 
public: 
    virtual concrete_product *create() const 
    { 
     return new concrete_product; 
    } 
    ... 
}; 
1

यह परिदृश्य जहां उपयोग एक ठोस कारखाने करना चाहते ठोस उत्पादों उत्पन्न करने के लिए उपयोगी हो जाता है। तुम हमेशा पर्याप्त रूप से सामान्य है सबसे विशेष इंटरफ़ेस चाहते हैं ...

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

13

सामान्य रूप से, कॉन्वर्सिस आपको बेस क्लास इंटरफ़ेस में सत्य की तुलना में व्युत्पन्न क्लास इंटरफ़ेस में अधिक जानकारी व्यक्त करने की अनुमति देता है। व्युत्पन्न वर्ग का व्यवहार आधार वर्ग की तुलना में अधिक विशिष्ट है, और अंतर भिन्नता (अंतर का एक पहलू) व्यक्त करता है।

जब आप गब्बिन्स के पदानुक्रमों से संबंधित होते हैं, तो यह उपयोगी होता है, जहां कुछ ग्राहक बेस क्लास इंटरफ़ेस का उपयोग करना चाहते हैं, लेकिन अन्य क्लाइंट व्युत्पन्न क्लास इंटरफ़ेस का उपयोग करेंगे।स्थिरांक-शुद्धता लोप के साथ:

class URI { /* stuff */ }; 

class HttpAddress : public URI { 
    bool hasQueryParam(string); 
    string &getQueryParam(string); 
}; 

class Resource { 
    virtual URI &getIdentifier(); 
}; 

class WebPage : public Resource { 
    virtual HttpAddress &getIdentifier(); 
}; 

ग्राहक जो जानते हैं कि वे एक वेबपृष्ठ है (ब्राउज़रों, शायद) पता है कि यह क्वेरी पैरामीटर को देखने के लिए सार्थक है। ग्राहक जो बेस बेस क्लास का उपयोग कर रहे हैं, ऐसी कोई चीज़ नहीं जानते हैं। वे हमेशा HttpAddress& को URI& परिवर्तनीय या अस्थायी रूप से बाध्य करेंगे।

यदि उन्हें संदेह है, लेकिन यह नहीं पता कि उनके संसाधन ऑब्जेक्ट में एक HttpAddress है, तो वे dynamic_cast कर सकते हैं। लेकिन कॉन्वर्सिस "सिर्फ जानने" से बेहतर है और उसी कारण से कास्ट कर रहा है कि स्थैतिक टाइपिंग बिल्कुल उपयोगी है।

विकल्प हैं - URI पर getQueryParam फ़ंक्शन को चिपकाएं लेकिन hasQueryParam सबकुछ के लिए झूठी वापसी करें (यूआरआई इंटरफ़ेस को अव्यवस्थित करें)। WebPage::getIdentifier को URL& लौटने के लिए परिभाषित किया गया है, वास्तव में HttpIdentifier& लौटा रहा है, और कॉलर्स एक व्यर्थ dynamic_cast (कॉलिंग कोड को बंद कर देते हैं, और वेबपृष्ठ का दस्तावेज़ीकरण करते हैं जहां आप कहते हैं कि "यूआरएल लौटाया गया है एचटीपीएड्रेस को गतिशील रूप से जा सकता है")। getHttpIdentifier फ़ंक्शन WebPage पर जोड़ें (WebPage इंटरफ़ेस को अव्यवस्थित करें)। या केवल इसका उपयोग करने के लिए कॉन्वर्सिस का उपयोग करें, जो इस तथ्य को व्यक्त करता है कि WebPage में FtpAddress या MailtoAddress नहीं है, इसमें HttpAddress है।

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

+2

गब्बिन्स? यह एक शब्द नहीं है जिसे मैं परिचित हूं। इसे गुगल करने से यह उत्पन्न होता है UrbanDictionary.com: \t एक gubbin कोई है जो इस अवसर पर एक आवारा की तरह काम करता है "आप विश्वास नहीं करेंगे उनके माता-पिता खुद एक हवेली होगा आप gubbin?!" मुझे लगता है कि वहाँ एक में एक अलग अर्थ है। प्रोग्रामिंग संदर्भ? –

+0

'fraid नहीं। यह बस मतलब "सामान", "चीजें"। –

1

मैं static_casts से छुटकारा पाने के लिए मौजूदा कोड के साथ काम करते समय अक्सर अपने आप को covariance का उपयोग कर पाते हैं। आम तौर पर स्थिति इस के समान है:

class IPart {}; 

class IThing { 
public: 
    virtual IPart* part() = 0; 
}; 

class AFooPart : public IPart { 
public: 
    void doThis(); 
}; 

class AFooThing : public IThing { 
    virtual AFooPart* part() {...} 
}; 

class ABarPart : public IPart { 
public: 
    void doThat(); 
}; 

class ABarThing : public IThing { 
    virtual ABarPart* part() {...} 
};  

यह

AFooThing* pFooThing = ...; 
pFooThing->Part()->doThis(); 

और

ABarThing pBarThing = ...; 
pBarThing->Part()->doThat(); 
बजाय

static_cast<AFooPart>(pFooThing->Part())->doThis(); 

और

की

करने के लिए मुझे अनुमति देता है 10

static_cast<ABarPart>(pBarThing->Part())->doThat(); 

अब ऐसे कोड में आने पर मूल डिजाइन के बारे में बहस हो सकती है और क्या कोई बेहतर है - लेकिन मेरे अनुभव में अक्सर प्राथमिकताओं, लागत/लाभ इत्यादि जैसी बाधाएं होती हैं जो व्यापक डिजाइन सौंदर्यीकरण में हस्तक्षेप करती हैं और इस तरह के केवल छोटे कदमों की अनुमति दें।

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