2008-08-29 17 views
10

क्या सी ++ में वर्चुअल स्टेटिक सदस्य का कोई तरीका है?सी ++ में कक्षा के वर्चुअल स्थिर सदस्य को सिम्युलेट करना?

उदाहरण के लिए:

class BaseClass { 
    public: 
     BaseClass(const string& name) : _name(name) {} 
     string GetName() const { return _name; } 
     virtual void UseClass() = 0; 
    private: 
     const string _name; 
}; 


class DerivedClass : public BaseClass { 
    public: 
     DerivedClass() : BaseClass("DerivedClass") {} 
     virtual void UseClass() { /* do something */ } 
}; 

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

class BaseClass { 
    public: 
     BaseClass() {} 
     virtual string GetName() const = 0; 
     virtual void UseClass() = 0; 
}; 


class DerivedClass : public BaseClass { 
    public: 
     DerivedClass() {} 
     virtual string GetName() const { return _name; } 
     virtual void UseClass() { /* do something */ } 
    private: 
     static const string _name; 
}; 

string DerivedClass::_name = "DerivedClass"; 

यह समाधान मुझे संतुष्ट नहीं है क्योंकि मैं सदस्य नाम_ और उसके एक्सेसर getName() हर कक्षा में reimplement की जरूरत है। मेरे मामले में मेरे पास कई सदस्य हैं जो _name व्यवहार और व्युत्पन्न कक्षाओं के दसवें अनुसरण करते हैं।

कोई विचार?

उत्तर

8

यहाँ एक समाधान है:

struct BaseData 
{ 
    const string my_word; 
    const int my_number; 
}; 

class Base 
{ 
public: 
    Base(const BaseData* apBaseData) 
    { 
     mpBaseData = apBaseData; 
    } 
    const string getMyWord() 
    { 
     return mpBaseData->my_word; 
    } 
    int getMyNumber() 
    { 
     return mpBaseData->my_number; 
    } 
private: 
    const BaseData* mpBaseData; 
}; 

class Derived : public Base 
{ 
public: 
    Derived() : Base(&sBaseData) 
    { 
    } 
private: 
    static BaseData sBaseData; 
} 

BaseData Derived::BaseData = { "Foo", 42 }; 
2

ऐसा लगता है कि उत्तर प्रश्न में है - आपके द्वारा सुझाई गई विधि जाने का सही दिशा प्रतीत होता है, सिवाय इसके कि यदि आपके पास उन साझा सदस्यों की बड़ी संख्या है तो आप उन्हें एक संरचना या कक्षा में इकट्ठा करना चाहते हैं और पिछले वर्ग के निर्माता के लिए तर्क के रूप में अतीत।

यदि आप व्युत्पन्न वर्ग के स्थिर सदस्यों के रूप में लागू "साझा" सदस्यों को रखने का आग्रह करते हैं, तो आप व्युत्पन्न कक्षाओं के कोड को स्वतः उत्पन्न करने में सक्षम हो सकते हैं। एक्सएसएलटी ऑटो-जनरेटिंग सरल वर्गों के लिए एक शानदार टूल है।

सामान्य रूप से, उदाहरण "आभासी स्थैतिक" सदस्यों की आवश्यकता नहीं दिखाता है, क्योंकि इन प्रयोजनों के लिए आपको वास्तव में विरासत की आवश्यकता नहीं होती है - इसके बजाय आपको बेस क्लास का उपयोग करना चाहिए और इसे उचित मानों को स्वीकार करना चाहिए कन्स्ट्रक्टर - हो सकता है कि प्रत्येक "उप-प्रकार" के लिए तर्कों का एक उदाहरण बनाये और साझा डेटा के डुप्लिकेशंस से बचने के लिए इसे पॉइंटर पास कर दिया जाए। एक और समान दृष्टिकोण टेम्पलेट्स का उपयोग करना और टेम्पलेट तर्क को एक वर्ग के रूप में पास करना है जो सभी प्रासंगिक मान प्रदान करता है (इसे आमतौर पर "नीति" पैटर्न के रूप में जाना जाता है)।

निष्कर्ष निकालने के लिए - मूल उदाहरण के उद्देश्य के लिए, ऐसे "वर्चुअल स्थिर" सदस्यों की कोई आवश्यकता नहीं है। यदि आपको अभी भी लगता है कि आपके द्वारा लिखे गए कोड के लिए उन्हें जरूरी है, तो कृपया विस्तृत करने और अधिक संदर्भ जोड़ने का प्रयास करें।

क्या मैं ऊपर वर्णित का उदाहरण:

class BaseClass { 
    public: 
     BaseClass(const Descriptor& desc) : _desc(desc) {} 
     string GetName() const { return _desc.name; } 
     int GetId() const { return _desc.Id; } 
     X GetX() connst { return _desc.X; } 
     virtual void UseClass() = 0; 
    private: 
     const Descriptor _desc; 
}; 


class DerivedClass : public BaseClass { 
    public: 
     DerivedClass() : BaseClass(Descriptor("abc", 1,...)) {} 
     virtual void UseClass() { /* do something */ } 
}; 

class DerDerClass : public BaseClass { 
    public: 
     DerivedClass() : BaseClass("Wowzer", 843,...) {} 
     virtual void UseClass() { /* do something */ } 
}; 

मैं इस समाधान पर विस्तृत करने, और शायद de-प्रारंभ समस्या का समाधान देने के लिए करना चाहते हैं:

एक छोटा सा परिवर्तन के साथ

, आप व्युत्पन्न वर्ग के प्रत्येक उदाहरण के लिए "वर्णक" के एक नए उदाहरण के बिना उपरोक्त वर्णित डिज़ाइन को कार्यान्वित कर सकते हैं।

आप एक सिंगलटन वस्तु, DescriptorMap, कि प्रत्येक डिस्क्रिप्टर का एक उदाहरण का आयोजन करेगा बना सकते हैं और इसका इस्तेमाल करते हैं जब इतनी तरह ली गई वस्तुओं का निर्माण:

:

enum InstanceType { 
    Yellow, 
    Big, 
    BananaHammoc 
} 

class DescriptorsMap{ 
    public: 
     static Descriptor* GetDescriptor(InstanceType type) { 
      if (_instance.Get() == null) { 
       _instance.reset(new DescriptorsMap()); 
      } 
      return _instance.Get()-> _descriptors[type]; 
     } 
    private: 
     DescriptorsMap() { 
      descriptors[Yellow] = new Descriptor("Yellow", 42, ...); 
      descriptors[Big] = new Descriptor("InJapan", 17, ...) 
      ... 
     } 

     ~DescriptorsMap() { 
      /*Delete all the descriptors from the map*/ 
     } 

     static autoptr<DescriptorsMap> _instance; 
     map<InstanceType, Descriptor*> _descriptors; 
} 

अब हम ऐसा कर सकते हैं

class DerivedClass : public BaseClass { 
    public: 
     DerivedClass() : BaseClass(DescriptorsMap.GetDescriptor(InstanceType.BananaHammoc)) {} 
     virtual void UseClass() { /* do something */ } 
}; 

class DerDerClass : public BaseClass { 
    public: 
     DerivedClass() : BaseClass(DescriptorsMap.GetDescriptor(InstanceType.Yellow)) {} 
     virtual void UseClass() { /* do something */ } 
}; 

निष्पादन के अंत में, जब सी रनटाइम अनियंत्रण करता है, तो यह हमारे ऑटोप्रेट समेत स्थैतिक वस्तुओं के विनाशक को भी बुलाता है, जो वर्णनकर्ताओं के हमारे उदाहरण को हटा देता है।

तो अब हमारे पास प्रत्येक वर्णनकर्ता का एक उदाहरण है जिसे निष्पादन के अंत में भी हटाया जा रहा है।

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

1

@Hershi: उस दृष्टिकोण के साथ समस्या यह है कि प्रत्येक व्युत्पन्न वर्ग के प्रत्येक उदाहरण में डेटा की एक प्रति होती है, जो किसी भी तरह से महंगा हो सकती है।

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


#include <iostream> 
#include <string> 
using namespace std; 

struct DerivedData 
{ 
    DerivedData(const string & word, const int number) : 
    my_word(word), my_number(number) {} 
    const string my_word; 
    const int my_number; 
}; 

class Base { 
public: 
    Base() : m_data(0) {} 
    string getWord() const { return m_data->my_word; } 
    int getNumber() const { return m_data->my_number; } 
protected: 
    DerivedData * m_data; 
}; 


class Derived : public Base { 
public: 
    Derived() : Base() { 
    if(Derived::s_data == 0) { 
     Derived::s_data = new DerivedData("abc", 1); 
    } 
    m_data = s_data; 
    } 
private: 
    static DerivedData * s_data; 
}; 


DerivedData * Derived::s_data = 0; 

int main() 
{ 
    Base * p_b = new Derived(); 
    cout getWord() << endl; 
} 

स्थिर ऑब्जेक्ट को हटाने पर अनुवर्ती प्रश्न के बारे में: एकमात्र समाधान है कि मन में आता है एक स्मार्ट सूचक, Boost shared pointer की तरह कुछ का उपयोग करने के लिए है।

1

मैं "बेस क्लास" के रूप में टेम्पलेट का उपयोग करने के लिए हर्षी के सुझाव से सहमत हूं। जो आप वर्णन कर रहे हैं, उससे यह टेम्पलेट्स के लिए उपयोग की तरह लगता है, फिर उपclassing।

आप एक टेम्पलेट के रूप में निम्नानुसार बना सकते हैं (यह संकलन करने प्रयास नहीं किया है):

 

template <typename T> 
class Object 
{ 
public: 

    Object(const T& newObject) : yourObject(newObject) {} ; 
    T GetObject() const { return yourObject } ; 
    void SetObject(const T& newObject) { yourObject = newObject } ; 

protected: 

    const T yourObject ; 
} ; 

class SomeClassOne 
{ 
public: 

    SomeClassOne(const std::vector& someData) 
    { 
    yourData.SetObject(someData) ; 
    } 

private: 

    Object<std::vector<int>> yourData ; 
} ; 
 

यह आपको डेटा का उपयोग करने वाले अपने कस्टम वर्गों के भीतर से जरूरत के रूप में संशोधित करने के लिए टेम्पलेट वर्ग तरीकों का उपयोग करने देगा डेटा और टेम्पलेट वर्ग के विभिन्न पहलुओं को साझा करें।

आप वंशानुक्रम का उपयोग करने पर आमादा हैं, तो आप अपने BaseClass में एक शून्य * सूचक का उपयोग कर और कास्टिंग से निपटने का "खुशियों" का सहारा लेना पड़ सकता है, आदि

हालांकि, अपने विवरण के आधार पर ऐसा लगता है कि आपको टेम्पलेट की आवश्यकता है और विरासत नहीं है।

0

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

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