2012-09-22 14 views
13

cppreference.com के अनुसार, std::shared_ptr सापेक्ष ऑपरेटरों (==,! =, <, ...) का पूरा सेट प्रदान करता है, लेकिन तुलना के अर्थशास्त्र निर्दिष्ट नहीं हैं। मुझे लगता है कि वे अंतर्निहित कच्चे पॉइंटर्स को संदर्भित वस्तुओं से तुलना करते हैं, और वह std :: weak_ptr और std :: unique_ptr वही करता है।क्या यह सी ++ 11 स्मार्ट पॉइंटर्स से प्राप्त करने के लिए ठीक है और रिश्तेदार ऑपरेटरों को ओवरराइड करता है?

कुछ उद्देश्यों के लिए, मैं रिश्तेदार ऑपरेटरों को पसंद करना चाहूंगा जो संदर्भित ऑब्जेक्ट्स (पॉइंटर्स के बजाए) की तुलना में स्मार्ट पॉइंटर्स को ऑर्डर करें। यह पहले से ही कुछ है जो मैं बहुत करता हूं, लेकिन अपने स्वयं के "गूंगा पॉइंटर्स" के साथ जो रिश्तेदार ऑपरेटरों को छोड़कर कच्चे पॉइंटर्स की तरह व्यवहार करते हैं। मैं मानक सी ++ 11 स्मार्ट पॉइंटर्स के साथ भी वही काम करना चाहता हूं। तो ...

  1. यह सी ++ 11 स्मार्ट संकेत (shared_ptr, weak_ptr और unique_ptr) से विरासत के लिए और रिश्तेदार ऑपरेटरों को ओवरराइड ठीक है?

  2. क्या कोई स्नीकी समस्या है जिसके लिए मुझे देखने की ज़रूरत है? उदाहरण के लिए, क्या चीजों को सही ढंग से काम करने के लिए using लागू करने या उपयोग करने के लिए मुझे कोई अन्य विधियां हैं?

  3. आलस्य में अंतिम के लिए, क्या एक लाइब्रेरी टेम्पलेट उपलब्ध है जो मेरे लिए यह स्वचालित रूप से करेगा?

मुझे आशा है कि यह एक "निश्चित रूप से आप ऐसा कर सकते हैं, मूर्ख!" चीज की तरह, लेकिन मैं थोड़ा अनिश्चित हूं क्योंकि मानक पुस्तकालय में कुछ कक्षाएं हैं (कम से कम std::map जैसे कंटेनर) जिन्हें आप से प्राप्त नहीं किया जाना चाहिए।

+3

सामान्य रूप से, यह किसी भी चीज से उत्तराधिकारी नहीं है जो विनाशक गतिशील नहीं है। यह सामान्य रूप से किया जा सकता है और _is_ किया जाता है, आपको सिर्फ _really_ सावधान रहना होगा। –

+0

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

+1

एर, "गतिशील" द्वारा, मेरा मतलब है "वर्चुअल" –

उत्तर

8

सामान्य रूप से, यह किसी भी चीज से उत्तराधिकारी नहीं है जो विनाशक गतिशील नहीं है। यह आमतौर पर किया जा सकता है और किया जाता है, आपको बस सावधान रहना होगा। पॉइंटर्स से विरासत के बजाय, मैं केवल रचना का उपयोग करता हूं, खासकर जब सदस्यों की संख्या अपेक्षाकृत कम होती है। आप जाहिर है इस

template<class pointer_type> 
class relative_ptr { 
public: 
    typedef typename std::pointer_traits<pointer_type>::pointer pointer; 
    typedef typename std::pointer_traits<pointer_type>::element_type element_type; 
    relative_ptr():ptr() {} 
    template<class U> 
    relative_ptr(U&& u):ptr(std::forward<U>(u)) {} 
    relative_ptr(relative_ptr<pointer>&& rhs):ptr(std::move(rhs.ptr)) {} 
    relative_ptr(const relative_ptr<pointer>& rhs):ptr(std::move(rhs.ptr)) {} 

    void swap (relative_ptr<pointer>& rhs) {ptr.swap(rhs.ptr);} 
    pointer release() {return ptr.release();} 
    void reset(pointer p = pointer()) {ptr.reset(p);} 
    pointer get() const {return ptr.get();} 
    element_type& operator*() const {return *ptr;} 
    const pointer_type& operator->() const {return ptr;} 

    friend bool operator< (const relative_ptr& khs, const relative_ptr& rhs) const 
    {return std::less<element>(*lhs,*rhs);} 
    friend bool operator<=(const relative_ptr& khs, const relative_ptr& rhs) const 
    {return std::less_equal<element>(*lhs,*rhs);} 
    friend bool operator> (const relative_ptr& khs, const relative_ptr& rhs) const 
    {return std::greater<element>(*lhs,*rhs);} 
    friend bool operator>=(const relative_ptr& khs, const relative_ptr& rhs) const 
    {return std::greater_equal<element>(*lhs,*rhs);} 
    friend bool operator==(const relative_ptr& khs, const relative_ptr& rhs) const 
    {return *lhs==*rhs;} 
    friend bool operator!=(const relative_ptr& khs, const relative_ptr& rhs) const 
    {return *lhs!=*rhs;} 
protected: 
    pointer_type ptr; 
}; 

के लिए एक टेम्पलेट वर्ग बनाने के लिए सक्षम हो सकती है, आवरण की सादगी स्मार्ट संकेत दिए गए हैं, लेकिन जो भी के लिए सबसे कम आम विभाजक के लिए आप कम करता है। वे बिल्कुल जटिल नहीं हैं, आप प्रत्येक स्मार्ट पॉइंटर कक्षाओं में से एक बना सकते हैं।

मैं एक चेतावनी प्रदान करूंगा कि मुझे == काम पसंद नहीं है, क्योंकि यह दो ऑब्जेक्ट्स के लिए अलग-अलग ऑब्जेक्ट्स के लिए सच हो सकता है। जो कुछ भी लेकिन। मैंने कोड का परीक्षण भी नहीं किया है, यह कुछ कार्यों के लिए असफल हो सकता है, जैसे कि एक अद्वितीय_ptr होने पर प्रतिलिपि बनाने का प्रयास करना।

+0

"चूंकि यह दो बिंदुओं के लिए अलग-अलग वस्तुओं के लिए सच हो सकता है" - हाँ, लेकिन यह सामान्य उदाहरणों पर लागू होता है। इन्हें प्रॉक्सी के रूप में सोचें जो स्मार्ट पॉइंटर्स भी होते हैं - एक सूचक होने के कारण प्रदान किए गए अमूर्तता की तुलना में अधिक कार्यान्वयन विवरण है। – Steve314

+0

मुझे पहले यह कहना चाहिए था, लेकिन पॉइंटर्स के लिए सी ++ में भी उदाहरण है जो प्रॉक्सी की तरह कार्य करता है और इसे संदर्भित करने की आवश्यकता नहीं है - '&' संदर्भ ar ई को आत्म-निरोधक पॉइंटर्स भी कहा जाता है। – Steve314

+0

इस उपयोग के मामले में वास्तव में आवश्यक चीज़ों की तुलना में थोड़ी अधिक दृढ़ता से ... :) –

3

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

इस वजह से, वर्गों को आधार के रूप में कार्य करने का इरादा आमतौर पर प्रतिलिपि का समर्थन नहीं करना चाहिए। जब प्रतिलिपि आवश्यक है, तो उन्हें इसके बजाय Derived* clone() const override जैसे कुछ प्रदान करना चाहिए।

जिस समस्या को आप हल करने का प्रयास कर रहे हैं वह शायद चीजों को छोड़कर और ऐसे पॉइंटर्स के साथ काम करते समय कस्टम तुलनाकर्ताओं को प्रदान करके सबसे अच्छा संबोधित किया जाता है।

std::vector<std::shared_ptr<int>> ii = …; 
std::sort(begin(ii), end(ii), 
      [](const std::shared_ptr<int>& a, const std::shared_ptr<int>& b) { 
       return *a < *b; 
      }); 
8

पहली बात, जैसा कि अन्य ने पहले ही बताया है कि विरासत जाने का रास्ता नहीं है। बल्कि स्वीकार किए जाते हैं जवाब ने सुझाव जटिल आवरण से, मैं काफी सरल कुछ करने होगा: अपनी खुद की प्रकारों के लिए अपने स्वयं के तुलनित्र लागू:

namespace myns { 
struct mytype { 
    int value; 
}; 
bool operator<(mytype const& lhs, mytype const& rhs) { 
    return lhs.value < rhs.value; 
} 
bool operator<(std::shared_ptr<mytype> const & lhs, std::shared_ptr<mytype> const & rhs) 
{ 
    // Handle the possibility that the pointers might be NULL!!! 
    // ... then ... 
    return *lhs < *rhs; 
} 
} 

जादू, जिस पर सभी वास्तव जादू नहीं तर्क निर्भर है लुकअप (उर्फ कोनिंग लुकअप या एडीएल)। जब कंपाइलर फ़ंक्शन कॉल का सामना करता है तो यह लुकअप के लिए तर्कों का नामस्थान जोड़ देगा। यदि ऑब्जेक्ट्स टेम्पलेट का तात्कालिकता है, तो संकलक टेम्पलेट को तुरंत चालू करने के लिए उपयोग किए जाने वाले प्रकारों के नामस्थान भी जोड़ देगा। तो में:

int main() { 
    std::shared_ptr<myns::mytype> a, b; 
    if (a < b) {      // [1] 
     std::cout << "less\n"; 
    } else { 
     std::cout << "more\n"; 
    } 
} 

में [1], और क्योंकि a और b वस्तुओं रहे हैं उपयोगकर्ता परिभाषित प्रकार(*) ADL में शुरू होगा और यह देखने सेट करने के लिए दोनों std और myns जोड़ देगा। यह तो std::shared_ptr वह यह है कि के लिए operator< की मानक परिभाषा मिलेगा:

template<class T, class U> 
bool std::operator<(shared_ptr<T> const& a, shared_ptr<U> const& b) noexcept; 

और यह भी myns जोड़ सकते हैं और जोड़ देगा: यह

bool myns::operator<(mytype const& lhs, mytype const& rhs); 

फिर, देखने पूर्ण होने के बाद, अधिभार संकल्प में लात मारता है, और यह निर्धारित करेगा कि myns::operator< कॉल के लिए std::operator< से बेहतर मिलान है, क्योंकि यह एक आदर्श मैच है और उस स्थिति में गैर-टेम्पलेट प्राथमिकता लेते हैं। इसके बाद यह मानक के बजाय अपना खुद का operator< कॉल करेगा।

यदि आपका प्रकार वास्तव में एक टेम्पलेट है, तो यह थोड़ा और अधिक हो जाता है, यदि कोई है, तो एक टिप्पणी छोड़ दो और मैं जवाब का विस्तार करूंगा।


(*) यह एक मामूली सरलीकरण है। क्योंकि operator< को सदस्य फ़ंक्शन या फ़्री फ़ंक्शन दोनों के रूप में कार्यान्वित किया जा सकता है, इसलिए संकलक std::shared_ptr<> के अंदर सदस्य operator< (मानक में मौजूद नहीं) और दोस्तों के लिए जांच करेगा। यह के अंदर friend फ़ंक्शंस के लिए भी देखेंगे ... और इसी तरह। लेकिन अंत में यह सही मिलेगा।

+0

मैं उचित होने पर ऐसा करता हूं, लेकिन जैसा कि मैंने विभिन्न टिप्पणियों में बताया है, यह और अधिक चीजें हैं जो संभवतः मिश्रित हो जाती हैं। जब आप जानते हैं कि दोनों चीजों का एक साथ उपयोग करना सही है, तो उन्हें एक साथ पैकेजिंग सुनिश्चित करता है कि वे एक साथ यात्रा करते हैं, और स्थैतिक टाइपिंग अतिरिक्त चेक प्रदान करती है। और जबकि "पॉइंटर" नाम भ्रामक हो सकता है - नेटवर्किंग के मुद्दों की कमी के बावजूद यह एक प्रकार का प्रॉक्सी है - इसका मतलब यह नहीं है कि कार्यक्षमता पैकेजिंग एक बुरी चीज है। – Steve314

+0

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

+2

इस समाधान में * कुछ * पास * * पास नहीं है। ऑपरेटर को आपके प्रकार के नामस्थान में * एक बार * परिभाषित किया जाता है, और वे कॉल के स्थान पर किसी भी अतिरिक्त कोड के बिना * हर जगह *, * impicitly * उपलब्ध हैं। यह इससे आसान नहीं हो सकता है। –

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