2013-02-20 90 views
5

मान लीजिए मेरे पास एक क्लास टेम्पलेट है जिसमें सदस्य pData है, जो AxB अरबी प्रकार T की सरणी है।यदि कथन के बाहर वर्ग टेम्पलेट का उदाहरण कैसे प्राप्त करें? (सी ++)

template <class T> class X{ 
public: 
    int A; 
    int B; 
    T** pData; 
    X(int a,int b); 
    ~X();   
    void print(); //function which prints pData to screen 

}; 
template<class T>X<T>::X(int a, int b){ //constructor 
    A = a; 
    B = b; 
    pData = new T*[A]; 
    for(int i=0;i<A;i++) 
     pData[i]= new T[B]; 
    //Fill pData with something of type T 
} 
int main(){ 
    //... 
    std::cout<<"Give the primitive type of the array"<<std::endl; 
    std::cin>>type; 
    if(type=="int"){ 
     X<int> XArray(a,b); 
    } else if(type=="char"){ 
     X<char> Xarray(a,b); 
    } else { 
     std::cout<<"Not a valid primitive type!"; 
    } // can be many more if statements. 
    Xarray.print() //this doesn't work, as Xarray is out of scope. 
} 

उदाहरण के तौर पर एक्सएआरई अगर किसी कथन के अंदर बनाया गया है, तो मैं इसे कहीं और उपयोग नहीं कर सकता। मैंने अगर बयानों से पहले एक पॉइंटर बनाने की कोशिश की लेकिन उस बिंदु पर पॉइंटर का प्रकार अज्ञात है, तो मैं सफल नहीं हुआ।

इस तरह की किसी समस्या से निपटने का एक उचित तरीका क्या होगा?

+0

इसका कोई स्पष्ट "उत्तर" नहीं है क्योंकि सी ++ स्थिर रूप से टाइप किया गया है। आप उपयोगकर्ता को किसी प्रकार के लिए संकेत नहीं दे सकते हैं और फिर उस प्रकार को बना सकते हैं और इसे कहीं और उपयोग कर सकते हैं - आपको संकलन समय पर प्रकार को जानना होगा! एक ऐसी तकनीक जो आम तौर पर इस स्थिति को संबोधित करती है वह "टाइप एरर" है, लेकिन इसके लिए आवश्यक है कि * आप * कुछ सामान्य तत्व निर्दिष्ट करें जो आपके सभी प्रकारों के पास हो, और केवल उस सामान्य तत्व (जैसे "प्रिंट करने योग्य") के माध्यम से इंटरफेस हो। –

+0

यहां, [यह] (http://stackoverflow.com/questions/1984492/runtime-determine-type-for-c) आपकी मदद कर सकता है। –

उत्तर

1

सी ++ एक स्थिर टाइप किया भाषा है, जिसका अर्थ है कि आप संकलन समय पर वस्तुओं के प्रकार पता होना चाहिए। इस मामले में आप उपयोगकर्ता इनपुट पर बनाए गए ऑब्जेक्ट के प्रकार का आधार बना रहे हैं, इसलिए रनटाइम पर प्रकार को जानना संभव नहीं है।

इस समस्या को हल करने का सबसे आम तरीका dynamic polymorphism का उपयोग करना है जिसमें late binding का उपयोग कर एक सामान्य इंटरफ़ेस के माध्यम से फ़ंक्शंस का उपयोग किया जाता है। हम इसे virtual functions का उपयोग कर सी ++ में पूरा करते हैं। उदाहरण के लिए:

struct IPrintable { 
    virtual void print() = 0; 
}; 

template<class T> 
class X : public IPrintable { 
    // Same code as you showed above. 
}; 

int main() { 
    std::cout<<"Give the primitive type of the array"<<std::endl; 
    std::cin>>type; 

    std::unique_ptr<IPrintable> XArray; 

    if(type=="int"){ 
     XArray.reset(new X<int>(a,b)); 
    } else if(type=="char"){ 
     XArray.reset(new X<char>(a,b)); 
    } else { 
     std::cout<<"Not a valid primitive type!"; 
    } // can be many more if statements. 

    Xarray->print() // this works now! 
} 

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

+0

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

+0

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

+0

कृपया ध्यान दें कि मैंने यह नहीं कहा कि यह एक खराब समाधान था। हम बस नहीं जानते (ओपी हमें उन सभी बाधाओं के बारे में नहीं बता रहा है जो उन्हें सामना करना पड़ता है)। आगंतुक * जटिल * नहीं हैं। यह वास्तव में सबसे कमजोर चीज़ों में से एक है जिसे मैंने कभी देखा है। यह सिर्फ इतना है कि मुझे नहीं लगता कि सी ++ सीखने वाले लोगों को यह सोचने के लिए प्रोत्साहित नहीं किया जाना चाहिए कि विरासत के साथ ऑब्जेक्ट उन्मुख प्रोग्रामिंग एकमात्र समाधान है: मुझे लगता है कि हर सप्ताह दर्जनों जावा प्रोग्रामर सी ++ में आते हैं जो सोचते हैं कि, और यह वास्तव में उबाऊ है । – ereOn

4

समस्या यह है कि X<int> और x<char> पूरी तरह से असंबंधित प्रकार हैं।

तथ्य यह है कि वे दोनों एक ही टेम्पलेट श्रेणी के परिणाम हैं, यहां सहायता नहीं करेंगे।

मैं कई समाधान देख सकता हूं, लेकिन जो आपको वास्तव में चाहिए उस पर निर्भर करता है।

उदाहरण के लिए, आप X<> उदाहरणों को एक सामान्य गैर-टेम्पलेटेड बेस क्लास से प्राप्त कर सकते हैं जिसमें print() विधि (अंततः शुद्ध वर्चुअल के रूप में) है। लेकिन ऐसा करने से पहले, सुनिश्चित करें कि यह एक कार्यात्मक स्तर पर समझ में आता है: किसी को विरासत का उपयोग करना चाहिए क्योंकि यह केवल तकनीकी बाधाओं के कारण नहीं, बल्कि समझ में आता है। और यदि आप ऐसा करते हैं, तो आप शायद आभासी विनाशक भी बनना चाहेंगे।

तुम भी बाँध सकता है और विधि आप कॉल करना चाहते करने के लिए एक std::function<void()> की दुकान है, लेकिन यह सुनिश्चित करें कि वस्तुओं अभी भी "जीवित" हैं (वे अपने वर्तमान कोड में नहीं हैं: दोनों X<int> और X<char> नष्ट कर रहे हैं, जब वे जाना दायरे से बाहर, वास्तव में print() पर कॉल करने से पहले)।

एक अंतिम समाधान कुछ प्रकार का प्रकार बनाना होगा जो X<int> और X<char> (boost::variant<> दोनों के साथ संगत है) के साथ संगत है। फिर आप एक आगंतुक लिख सकते हैं जो प्रत्येक प्रकार के लिए print() कार्यक्षमता लागू करता है।

पिछले समाधान चुनने, यह बन जाएगा कुछ की तरह:

typedef boost::variant<X<int>, X<char>> genericX; 

class print_visitor : public boost::static_visitor<void> 
{ 
public: 
    template <typename SomeType> 
    void operator()(const SomeType& x) const 
    { 
     // Your print implementation 
     // x is your underlying instance, either X<char> or X<int>. 
     // You may also make several non-templated overloads of 
     // this operator if you want to provide different implementations. 
    } 
}; 

int main() 
{ 
    boost::optional<genericX> my_x; 

    if (type=="int") { 
    my_x = X<int>(a,b); 
    } else if(type=="char") { 
    my_x = X<char>(a,b); 
    } 

    // This calls the appropriate print. 
    if (my_x) { 
    boost::apply_visitor(print_visitor(), *my_x) 
    } 
} 

हम वास्तव में ज्ञान एक निश्चित जवाब देने के लिए की कमी है: अगर अपनी कक्षाओं "संस्थाओं" कर रहे हैं, तो आप शायद विरासत के लिए जाना चाहिए। यदि वे "मूल्य वर्ग" की तरह अधिक हैं, तो संस्करण तरीका अधिक उपयुक्त हो सकता है।

1

यदि आप विभिन्न सरणी के साथ काम करना चाहते हैं, तो जो भी उनके प्रकार, टेम्पलेट अकेले आपकी मदद नहीं कर सकते हैं। वर्तमान में, X<int> और X<char> के बीच बिल्कुल कोई संबंध नहीं है।

यदि आप उन्हें एक सामान्य प्रकार के दो उपप्रकारों के रूप में देखना चाहते हैं, तो आपको विरासत (और गतिशील रूप से आवंटित चर) का उपयोग करना होगा। उदाहरण के लिए, सभी X<T>, एक ही आधार वर्ग को प्राप्त कर सके Printable कहते हैं, और आप एक unique_ptr<Printable> में डाटा स्टोर कर सकते हैं:

unique_ptr<Printable> r; 
if(type=="int"){ 
    r.reset(new X<int>(a,b)); 
} else if(type=="char"){   
    r.reset(new X<char>(a,b); 
} 
r->print(); 

लेकिन यह शायद सबसे अच्छा डिजाइन नहीं है।

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

template<class T> 
void workWithType(int a, int b) 
{ 
    X<T> Xarray(a, b); 
    Xarray.print(); 
} 

//... 

if(type=="int"){ 
    workWithType<int>(a,b); 
} else if(type=="char"){ 
    workWithType<char>(a,b); 
} 
+0

विरासत का अर्थ यह नहीं है कि उसे गतिशील आवंटन का उपयोग करना होगा। – ereOn

1

बल्कि main में टेम्पलेट्स फिट करने के लिए मैं सुझावों में से बाकी की तुलना में विपरीत मदद मिलेगी ... कोड ले जाने के बाहर main की और में अपने आप (संभवतः टेम्प्लेटेड) समारोह है कि के साथ सौदा करने की जरूरत है कोशिश कर रहा से एक प्रकार का:

template <typename T> 
void generateAndPrint(int a, int b) { 
    X<T> x(a,b); 
    x.print(); 
} 
int main() { ... 
    if (type=="int") generateAndPrint<int>(a,b); 
    else if (type=="char") generateAndPrint<char>(a,b); 
    else ... 
}