2009-04-02 23 views
7

मैं सी ++ एक नोटिफ़ायर क्लास में बनाना चाहता हूं जिसे ऑब्जेक्ट नष्ट होने पर विभिन्न धारकों को सूचित करने के लिए मैं अन्य ऑब्जेक्ट्स में उपयोग करूंगा।मैं सी ++ में मालिक ऑब्जेक्ट का पता कैसे जान सकता हूं?

template <class Owner> 
class Notifier<Owner> { 
public: 
    Notifier(Owner* owner); 
    ~Notifier(); // Notifies the owner that an object is destroyed 
}; 

class Owner; 

class Owned { 
public: 
    Owned(Owner* owner); 
private: 
    Notifier<Owner> _notifier; 
}; 

मेरे बिंदु के रूप में मैं एक घने और जटिल वस्तु ग्राफ है, मैं सूचक में स्वामित्व वस्तु का पता भंडारण से बचना चाहते हैं। क्या मेरे नोटिफ़ायर क्लास को बदलने का कोई तरीका है ताकि वह स्वामित्व वाले ऑब्जेक्ट के पते को अपने पते से हटा दे और ऑफसेट जिसे संकलित समय पर गणना की जाएगी?

ध्यान दें कि किसी ऑब्जेक्ट को संभवतः उसी वर्ग से कई 'मालिकों' को सूचित करना पड़ सकता है।

धन्यवाद।

+0

क्या आपको स्थैतिक बहुरूपता की आवश्यकता है या आप एक अमूर्त बेस क्लास IOwner बना सकते हैं उदा। शुद्ध वर्चुअल विधि 'सूचित करें'? –

+0

नहीं, मेरे पास शुद्ध आभासी 'अधिसूचना' नहीं हो सकती है। –

उत्तर

2

या कुछ इस तरह: अपने सूचक से

इनहेरिट और टेम्पलेट पैरामीटर के रूप में स्वामित्व जोड़ सकते हैं।

template < class Owner , class Owned > 
class Notifier 
{ 
public: 
    Notifier(Owner* owner) 
    {} 

    Owned * owned() 
    { return static_cast< Owned * >(this); } 

    ~Notifier() 
    { 
     // notify owner with owned() 
    } 
}; 

class Owner 
{}; 

class Owned : public Notifier< Owner , Owned > 
{ 
public: 
    Owned(Owner * owner) : Notifier< Owner , Owned >(owner) 
    {} 
}; 
+0

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

+0

@Luc: हां, यह एक ही प्रकार के एकाधिक मालिकों को बस थोड़ा और काम करने की इजाजत देता है: उदा। एक int टेम्पलेट पैरामीटर ... मेरा जवाब देखें। –

+0

यह समाधान केवल तभी लागू होता है यदि ऑब्जेक्ट ग्राफ़ लगभग स्थिर है (उसी प्रकार के एक से अधिक ऑब्जेक्ट नहीं)। यदि आप पेड़ की तरह गतिशील रूप से उदाहरण बनाते हैं तो आपको मालिक के उदाहरणों को संग्रहीत करने की आवश्यकता होती है। – mmmmmmmm

1

समाधान का हिस्सा नोटिफ़ायर से प्राप्त उत्तराधिकारी होना होगा। इस तरह, नष्ट वस्तु का पता बस है 'इस' ...

class Owned : public Notifier<Owner> { 
public: 
    Owned(Owner* owner) 
    : Notifier<Owner>(owner) 
    {} 
}; 

लेकिन यह कैसे एक ही कक्षा से कई 'मालिकों' को संभालने के लिए? 'एक ही कक्षा' से कई बार कैसे प्राप्त किया जा सकता है?

fa's answer के लिए धन्यवाद, यहाँ समाधान मैं खोज रहा था है:

#include <iostream> 

template <class Owner, class Owned, int = 0> 
class Notifier { 
public: 
    Notifier(Owner* owner) 
    : _owner(owner) 
    {} 
    ~Notifier() { 
    _owner->remove(owned()); 
    } 
    Owned * owned(){ 
    return static_cast< Owned * >(this); 
    } 

private: 
    Owner* _owner; 
}; 

class Owner { 
public: 
    void remove(void* any) { 
    std::cout << any << std::endl; 
    } 
}; 

class Owned : public Notifier<Owner,Owned,1>, Notifier<Owner,Owned,2> { 
public: 
    Owned(Owner* owner1, Owner* owner2) 
    : Notifier<Owner,Owned,1>(owner1) 
    , Notifier<Owner,Owned,2>(owner2) 
    {} 
}; 

int main() { 
    std::cout << sizeof(Owned) << std::endl; 
    Owner owner1; 
    Owner owner2; 
    Owned owned(&owner1, &owner2); 
    std::cout << "Owned:" << (void*)&owned << std::endl << std::endl; 
} 

धन्यवाद!

+0

नोटिफ़ायर में, क्या आप एक के बजाय मालिकों की एक सूची स्टोर नहीं कर सकते? –

+0

आपका उपयोग केस वास्तव में संकलन समय पर यह सब जानने के लिए अनुमति देता है? और आप इतने सारे टेम्पलेट्स को तुरंत चालू करने से कोड ब्लोट के ठीक हैं? – rmeador

+0

उपयोग केस बिजनेस ऑब्जेक्ट मॉडल में कक्षाओं के बीच संबंधों को लागू करना है। जैसे एक ग्राहक से एक मांग जुड़ी हुई है: मैं उन वर्गों के बारे में सब कुछ जानता हूं, और मुझे पता है कि प्रत्येक मांग में वास्तव में 1 ग्राहक है, और प्रत्येक ग्राहक के पास 0 से एन मांग है ... –

6

GoF Observer Design Patter पर एक नज़र डालें।

+0

मैं वास्तव में लागू करने का एक तरीका ढूंढ रहा हूं एक से कई रिश्ते। यह पर्यवेक्षक पैटर्न के लिए बहुत अच्छी तरह से उपयोग किया जा सकता है, लेकिन जरूरी नहीं है। –

+0

@Xavier: मुझे यकीन नहीं है कि मैं आपको समझता हूं। "पर्यवेक्षक के लिए बहुत अच्छी तरह से इस्तेमाल किया जाना चाहिए"। पर्यवेक्षक वह तरीका है जिस पर आप अपनी अधिसूचना लागू कर सकते हैं। –

+0

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

0

मुझे अत्यधिक संदेह है। नोटिफ़ायर को यह जानने का कोई तरीका नहीं है कि इसका उपयोग रचना में किया गया है। क्या होगा यदि मैं

class Foo 
{ 
private: 
    Notifier _a, _b, _c; 
} 

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

+0

संकलन-समय पर यह जानकारी देने के लिए कोई भी टेम्पलेट (या अन्य) चाल है? –

+0

कोई तरीका नहीं है टेम्पलेट्स आपकी मदद कर सकते हैं। –

3

यह एक बुरा हैक हो सकता है और शायद काम करने के लिए गारंटी नहीं है, लेकिन यहां एक विचार मैं इस की सलाह नहीं देते है।

मान लीजिए कि आपके लेआउट की तरह आप इस तरह वर्णित: _notifier कि इसके नाम से जानता है

template <class Owner> 
class Notifier<Owner> { 
public: 
    Notifier(Owner* owner); 
    ~Notifier(); // Notifies the owner that an object is destroyed 
}; 

class Owner; 

class Owned { 
public: 
    Owned(Owner* owner); 
private: 
    Notifier<Owner> _notifier; 
}; 

है, यह Owned 'इस तरह के पते (जो Notifier में निष्पादित किया जाता है के निर्माता) की गणना कर सकते हैं:

Owned *p = reinterpret_cast<Owned *>(reinterpret_cast<char *>(this) - offsetof(Owned, _notifier)); 

मूल रूप से, धारणा यह है कि _notifier स्वामित्व वाले वर्ग के भीतर कुछ निश्चित ऑफसेट पर है। इसलिए स्वामित्व का पता _notifier के पते के बराबर है जो उसी ऑफसेट से कम है।

एक बार फिर, यह अनिर्धारित व्यवहार है जिसे मैं अनुशंसा नहीं करता, लेकिन संभवतः काम कर सकता हूं।

3

fa.'s answer एक अच्छी शुरुआत है: तो फिर तुम एक स्वामित्व विधि सूचक के अंदर उपलब्ध हो सकता है। हालांकि, यह एक ही प्रकार के एकाधिक मालिकों की समस्या का समाधान नहीं करता है। एक समाधान यह है कि नोटिफ़ायर को एक के बजाय मालिकों की एक सूची स्टोर करना है।

template <typename Owner, typename Owned> 
class Notifier 
{ 
    protected: 
    Notifier() 
    {} 

    // Constructor taking a single owner 
    Notifier(Owner & o) 
    { 
     owners.push_back(&o); 
    } 

    // Constructor taking a range of owners 
    template <typename InputIterator> 
    Notifier(InputIterator firstOwner, InputIterator lastOwner) 
     : owners(firstOwner, lastOwner) {} 

    ~Notifier() 
    { 
     OwnerList::const_iterator it = owners.begin(); 
     OwnerList::const_iterator end = owners.end(); 
     for (; it != end ; ++it) 
     { 
      (*it)->notify(static_cast<Owned*>(this)); 
     } 
    } 

    // Method for adding a new owner 
    void addOwner(Owner & o) 
    { 
     owners.push_back(&o); 
    } 

private: 
    typedef std::vector<Owner *> OwnerList; 
    OwnerList owners; 
}; 

आप इसे इस तरह से उपयोग कर सकते हैं: यहाँ एक त्वरित कार्यान्वयन विचार दिखाने के लिए है,

class Owner; 

class Owned : public Notifier<Owner, Owned> 
{ 
    typedef Notifier<Owner, Owned> base; 

    //Some possible constructors: 
    Owned(Owner & o) : base(o) { } 

    Owned(Owner & o1, Owner & o2) 
    { 
     base::addOwner(o1); //qualified call of base::addOwner 
     base::addOwner(o2); //in case there are other bases 
    } 

    Owned(std::list<Owner*> lo) : base(lo.begin(), lo.end()) { } 
}; 

मामले ऐसे हैं जिनमें मालिकों के विभिन्न प्रकार है, यह समाधान नहीं बल्कि मुश्किल हो सकता है उपयोग करने के लिए। होगा इस समाधान को लागू करने

class Owned : public Notifier<Owned, OwnerType1, OwnerType1, OwnerType2> 
{ 
    Owned(OwnerType1 & o1, OwnerType1 & o2, OwnerType2 & o3) 
     : base(o1,o2,o3) 
}; 

हालांकि, इस मामले में, आप को बढ़ावा देने metaprogramming पुस्तकालयों (MPL, Fusion), जिसके साथ आप एक कोड आप ऐसा सामान करते हैं कि लग सकती है को देखने के लिए चाहते हो सकता है पिछले एक से थोड़ा लंबा हो।

+0

यह बहुत दिलचस्प है अगर किसी को मालिकों की गतिशील सूची की आवश्यकता होती है। मेरे मामले में, मुझे पहले से पता है कि मेरे पास कितने मालिक हैं (आमतौर पर एक, कभी-कभी दो, कभी और नहीं)। –