2012-02-01 11 views
5

इंटरफ़ेस:सी ++ टेम्पलेट्स बहुरूपता बाधा

template <class T> 
class Interface{ 
    public: 
    typedef T Units; 
    virtual T get() = 0; 
}; 

Implementation1:

class Implementation1: public Interface<float> { 
    public: 

    float get() { 
     return 0.0f; 
    } 

}; 

Implementation2:

class Implementation2: public Interface<int> { 
    public: 

    int get() { 
     return 0; 
    } 

}; 

कंटेनर (त्रुटियों के साथ):

class Container{ 
    private: 
    Interface* floatGetter; 
    int n; 
    Timer::Units* array; 

    public: 
    Container(Interface* floatGetter, int n) { 
     this->floatGetter= floatGetter; 
     this->n = n; 
     array = new Timer::Units[n]; 
    } 

    ~Container() { 

    } 

}; 

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

Edit1:

उदाहरण:

Interface<float> myInterface1 = new Implementation1(); 
Interface<int> myInterface2 = new Implementation2(); 
Container container1 = new Container(myInterface1, 10); 
Container container2 = new Container(myInterface2, 10); 

मैं की जरूरत है कि कंटेनर इसके कार्यान्वयन से इंटरफेस टेम्पलेट तर्क को समझता है।

उत्तर

6

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

एक समाधान यह है कि प्रश्न में क्या किया गया है, यानी इंटरफ़ेस वर्ग का आधुनिकीकरण करें। टेम्पलेट प्रकारों की एक महत्वपूर्ण संपत्ति यह है कि एक ही कक्षा के विभिन्न तत्काल तत्काल अलग-अलग प्रकार हैं। वे एक आम आधार साझा नहीं करते हैं, और वे एक दूसरे के लिए परिवर्तनीय नहीं हैं। हमारे पास नियमित रूप से Interface<Generic> पॉइंटर नियमित कार्यों में चारों ओर नहीं जा सकता है, उनके प्राप्त() विधियों को बुलाया जा सकता है। इस पर विचार करें: इंटरफेस टेम्पलेट प्रकार के प्रत्येक इंस्टेंस में get() विधि के लिए एक अलग हस्ताक्षर है। इसका मतलब है कि जब उस विधि को बुलाया जा रहा है, तो स्टैक पर अलग-अलग चीजें होती हैं। कंपाइलर कैसे पता लगा सकता है कि() फ़ंक्शन कॉल के लिए स्टैक तैयार करने के लिए() फ़ंक्शन कॉल के लिए स्टैक कैसे तैयार करें() विधि का कौन सा संस्करण है, यदि उसके पास Interface<Generic> पॉइंटर है।

मैं उस समस्या के दो सामान्य समाधानों के बारे में सोच सकता हूं।

  1. निकालें सभी टेम्पलेट mumbo- जंबो और प्राप्त() विधि इस तरह बढ़ावा के रूप में एक प्रकार मिट वस्तु लौटने के लिए, :: या प्रकार को बढ़ावा देने :: किसी भी बनाते हैं। अगर मैं यहां गलत हूं तो मुझे सही करें (*), लेकिन बढ़ावा :: संस्करण एक संघ की तरह है जो याद करता है कि किस प्रकार का संघ सौंपा गया है, जबकि बढ़ावा :: कोई भी शून्य * जैसा है, लेकिन यह याद करता है कि यह किस प्रकार की ओर इशारा करता है । यह समाधान पथ दो चीजों का तात्पर्य है: ए) लौटाए गए ऑब्जेक्ट्स के प्रकार रनटाइम पर हल किए जाएंगे, और इन प्रकारों में हेरफेर करते समय कुछ ओवरहेड होंगे। बी) इंटरफेस के बच्चों के वर्गों को इन प्रकार के मिटाए गए ऑब्जेक्ट्स में से एक को प्रबंधित करना होगा, जिससे उन्हें और अधिक जटिल बना दिया जाएगा।

  2. चरम करने के लिए टेम्पलेट mumbo- जंबो लें और templetized संदर्भ में हमेशा वस्तुओं इंटरफ़ेस है, ताकि संकलक उत्पन्न करता है सही समारोह उन संदर्भों की instantiations दौरान कॉल देखें। मैंने नीचे एक उदाहरण दिया जो इस पथ का पालन करता है। उदाहरण विभिन्न प्रकार के इंटरफ़ेस <> ऑब्जेक्ट्स को एक साथ रखने के लिए एक कंटेनर बनाता है, जबकि मंदिरों के कार्यात्मक कार्यान्वयन के अनुप्रयोग को सक्षम करते हुए (क्या यह आम तौर पर इसे "आगंतुकों" कहने के लिए सही है?)। ध्यान दें कि उस उदाहरण में, विभिन्न प्रकार के पैरामीटर वाले इंटरफेस ऑब्जेक्ट्स वास्तव में उस कंटेनर क्लास में अलग-अलग std :: सूचियों में रखे जाते हैं, इसलिए रनटाइम में, उनके प्रकारों को हल करने की कोई आवश्यकता नहीं होती है।

अस्वीकरण: क्या इस प्रकार एक overkill है ...

यहाँ कैसे आप अलग टेम्पलेट तर्क के साथ "इंटरफेस" टेम्पलेट वर्ग के एक कंटेनर हो सकता है। उदाहरणों को रखने के लिए मैंने std :: सूची का उपयोग किया है, लेकिन आप इसे बदल सकते हैं।

#include<boost/fusion/container/vector.hpp> 
#include<boost/fusion/algorithm.hpp> 
#include<boost/mpl/transform.hpp> 
#include<boost/mpl/contains.hpp> 
#include<boost/utility/enable_if.hpp> 
#include<boost/type_traits/add_reference.hpp> 
#include<list> 
#include<algorithm> 
#include <iostream> 

using namespace boost; 

template <class T> 
class Interface{ 
    public: 
    typedef T Units; 
    virtual T get() = 0; 
}; 

class Implementation1: public Interface<float> { 
    public: 

    float get() { 
     return 0.0f; 
    } 

}; 

class Implementation2: public Interface<int> { 
    public: 

    int get() { 
     return 5; 
    } 

}; 

template<class element> 
struct to_list { 
    typedef std::list<Interface<element> *> type; 
}; 

template<class elementVector> 
struct to_containers { 
    typedef typename mpl::transform<elementVector,to_list<mpl::_1> >::type type; 
}; 

class Container{ 
    typedef fusion::vector<int,float> AllowedTypes; 
    typename to_containers<AllowedTypes>::type containers; 

public: 
    template<class type> typename enable_if<mpl::contains<AllowedTypes,type>,void>::type 
    /*void*/ add(Interface< type/*included in AllowedTypes*/ > & floatGetter) { 
     fusion::deref(fusion::find<typename to_list<type>::type >(containers)) 
      /*<type> container*/.push_back(&floatGetter); 
    } 

    template<class functional> 
    void apply(functional f) { 
     fusion::for_each(containers,applyFunctional<functional>(f)); 
    } 

private: 
    template<class functional> 
    struct applyFunctional { 
     functional f; 
     applyFunctional(functional f): f(f){} 
     template<class T> void operator()(T & in) const { 
      std::for_each(in.begin(), in.end(),f); 
     } 
    }; 

}; 

struct printValueFunctional { 
    template<class element> 
    void operator()(Interface<element> * in) const { 
     std::cout<<"Hi, my value is:"<<in->get()<<"\n"; 
    } 
}; 

int main() { 

    Implementation1 impl1; 
    Implementation2 impl2; 
    Interface<float> &myInterface1 = impl1; 
    Interface<int> &myInterface2 = impl2; 
    Container container; 
    container.add(myInterface1); 
    container.add(myInterface2); 
    container.apply(printValueFunctional()); 
    return 0; 
} 

और उत्पादन होता है:

Hi, my value is:5 
Hi, my value is:0 

खैर, यह वास्तव में सबसे अनुप्रयोगों के लिए एक बहुत बड़ा overkill है, लेकिन आप यह :) के लिए कहा

तुम सिर्फ एक अंतरफलक चाहते हैं, कि विभिन्न चीजें वापस कर सकते हैं, आप boost.variant पर भी विचार कर सकते हैं। ऊपर दिया गया उदाहरण सभी स्थैतिक बहुरूपता के लिए वास्तव में मूल्यवान है।

संपादित करें: डेविड ने कुछ महत्वपूर्ण बताया है, यह एक गड़बड़ी हो सकती है, अगर आप किसी कारण से अन्यथा मानते हैं। यह कंटेनर आइटम सम्मिलन के क्रम में वास्तव में सच नहीं रहता है। आपके कार्यात्मक कॉल का क्रम आइटम के सम्मिलन के क्रम में नहीं हो सकता है, यानी मान लें कि पुनरावृत्ति "यादृच्छिक" क्रम में होगी।

(*) को बढ़ावा देने :: संस्करण और बढ़ावा देने :: किसी भी here

+1

+1। मुझे नहीं लगता कि यह समस्या का एक अच्छा समाधान है, लेकिन यह प्रतिनिधि का हकदार है :) –

+0

धन्यवाद :) मुझे नहीं लगता कि यह सामान्य रूप से समस्या का एक अच्छा समाधान है, लेकिन यह दिखाता है कि टेम्पलेट मेटाप्रोग्रामिंग अनुमति देता है बिना किसी प्रकार के मिटाए। आपको बहुत तेजी से पुनरावृत्ति के साथ एक मिश्रित कंटेनर भी मिलता है। – enobayram

+0

यह वास्तव में एक मिश्रित कंटेनर नहीं है (या यह है?) ... लेकिन एक प्रकार जो कई कंटेनरों को आंतरिक रूप से रखता है। मेरे लिए अंतर इस तथ्य में है कि विभिन्न प्रकार अभी भी आंतरिक रूप से अलग हो गए हैं, भले ही आपके पास यह धारणा है कि वे नहीं हैं, और इसका मतलब है कि टाइप एरर के साथ आप कंटेनर इनवेरिएंट को बनाए रख सकते हैं (उदाहरण के लिए, सम्मिलन का क्रम अनुक्रम कंटेनरों में), आप इस दृष्टिकोण के साथ ऐसा नहीं कर सकते हैं (ईमानदार होने के लिए यह सिर्फ एक झटका है, मैंने कोड पर पढ़ा है, लेकिन संकलित/कोशिश नहीं की है) –

4

Interface एक टेम्पलेट है, एक प्रकार नहीं। अपनी कक्षा में चर, एक विशेष प्रकार के टेम्पलेट के इन्स्टेन्शियशन होना चाहिए के रूप में:

class Container { 
    Interface<float> *floatGetter; 

और इसी तरह निर्माता के तर्क के लिए।

साइड नोट: आपके विनाशक को आपके वर्ग को प्रबंधित करने वाले संसाधनों को मुक्त करना चाहिए।

साइड नोट 2: एक प्रकार लिखना काफी कठिन है जो एक से अधिक संसाधनों का प्रबंधन करता है, अपने डेटा को पकड़ने के लिए स्मार्ट पॉइंटर्स का उपयोग करने पर विचार करें।

साइड नोट 3: प्रारंभिक सूचियों को सीखें और उपयोग करें।

+0

आपका * निर्माता * संसाधनों मुक्त कर देना चाहिए चर्चा कर रहे हैं? –

+0

@jesse टाइपो को पकड़ने के लिए धन्यवाद ... बेशक विनाशक को कन्स्ट्रक्टर के बजाए संसाधनों को मुक्त करना चाहिए। –

+0

अद्यतन की तलाश करें, कृपया – itun

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