2012-02-27 27 views
15

सी ++ 11 के साथ, मैं खुद से पूछ रहा था कि अगर C++ 11 में boost :: ptr_containers का प्रतिस्थापन है। मुझे पता है कि मैं उदाहरण का उपयोग कर सकता हूँ एक std::vector<std::unique_ptr<T> >, लेकिन मुझे यकीन नहीं है कि यह एक पूर्ण प्रतिस्थापन है या नहीं। इन मामलों को संभालने का अनुशंसित तरीका क्या है?std :: unique_ptr के बनाम एसटीएल कंटेनर :: ptr_container

+2

'unique_ptr' के साथ आपको अभी भी नोड्स को कम करना होगा, लेकिन इसके अलावा उन्हें बहुत अधिक व्यवहार करना चाहिए। –

उत्तर

19

वे वास्तव में दो समान लेकिन विभिन्न समस्याओं को हल करते हैं।

एक सूचक कंटेनर एक कंटेनर में ऑब्जेक्ट्स को स्टोर करने का एक तरीका है जो मानकों की बजाय आवंटित स्मृति के पॉइंटर्स होने के लिए होता है। वे अपनी शक्ति में को छिपाते हैं कि तथ्य यह है कि वे पॉइंटर्स के कंटेनर हैं। इसका अर्थ है:

  • कंटेनर में प्रविष्टियां न्यूल नहीं हो सकती हैं।
  • मानदंडों और कार्यों से प्राप्त मान संदर्भ प्रकार के लिए हैं, प्रकार के पॉइंटर्स नहीं।
  • कई मानक एल्गोरिदम के साथ काम करना मुश्किल हो सकता है ... मुश्किल। और "मुश्किल" से, मेरा मतलब टूटा हुआ है। सूचक कंटेनरों का अपना अंतर्निर्मित एल्गोरिदम होता है।

हालांकि, तथ्य यह है कि सूचक कंटेनर पता है कि वे संकेत के कंटेनरों को कर रहे हैं, वे कुछ नई कार्यक्षमता की पेशकश कर सकते हैं:

  • एक clone सदस्य समारोह है कि एक गहरी प्रतिलिपि करता है, प्रयोग के माध्यम से ऑब्जेक्ट के प्रकार पर एक निश्चित "क्लोनेबल" अवधारणा का।
  • कंटेनर की अपनी वस्तुओं के स्वामित्व को जारी करने की क्षमता (उदाहरण के लिए एक उथली प्रतिलिपि के बाद)।
  • अन्य कंटेनरों में स्वामित्व स्थानांतरित करने के लिए अंतर्निहित फ़ंक्शंस।

वे वास्तव में काफी अलग अवधारणाएं हैं। ऐसी कई चीजें हैं जिन्हें आपको मैन्युअल रूप से करना होगा कि पॉइंटर कंटेनर स्वचालित कार्यों के साथ स्वचालित रूप से कर सकते हैं।

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

+1

मुझे लगता है कि आप कह सकते हैं कि उनमें से एक स्पष्ट रूप से पॉइंटर्स का कंटेनर है जबकि दूसरा पॉलिमॉर्फिक ऑब्जेक्ट्स के लिए एक कंटेनर है ... – Mehrdad

22

मैंने एक छोटा प्रोग्राम लिखने का फैसला किया जो कुछ पॉलिमॉर्फिक ऑब्जेक्ट्स को एक कंटेनर में रखता है (पॉइंटर से ढेर तक), और फिर उस कंटेनर को std :: एल्गोरिदम के साथ उपयोग करें। मैंने उदाहरण के तौर पर std::remove_if चुना है।

यहाँ कैसे मैं vector<unique_ptr<T>> साथ यह करना होगा:

#include <vector> 
#include <memory> 
#include <iostream> 

class Animal 
{ 
public: 
    Animal() = default; 
    Animal(const Animal&) = delete; 
    Animal& operator=(const Animal&) = delete; 
    virtual ~Animal() = default; 

    virtual void speak() const = 0; 
}; 

class Cat 
    : public Animal 
{ 
public: 
    virtual void speak() const {std::cout << "Meow\n";} 
    virtual ~Cat() {std::cout << "destruct Cat\n";} 
}; 

class Dog 
    : public Animal 
{ 
public: 
    virtual void speak() const {std::cout << "Bark\n";} 
    virtual ~Dog() {std::cout << "destruct Dog\n";} 
}; 

class Sheep 
    : public Animal 
{ 
public: 
    virtual void speak() const {std::cout << "Baa\n";} 
    virtual ~Sheep() {std::cout << "destruct Sheep\n";} 
}; 

int main() 
{ 
    typedef std::unique_ptr<Animal> Ptr; 
    std::vector<Ptr> v; 
    v.push_back(Ptr(new Cat)); 
    v.push_back(Ptr(new Sheep)); 
    v.push_back(Ptr(new Dog)); 
    v.push_back(Ptr(new Sheep)); 
    v.push_back(Ptr(new Cat)); 
    v.push_back(Ptr(new Dog)); 
    for (auto const& p : v) 
     p->speak(); 
    std::cout << "Remove all sheep\n"; 
    v.erase(
     std::remove_if(v.begin(), v.end(), 
         [](Ptr& p) 
          {return dynamic_cast<Sheep*>(p.get());}), 
     v.end()); 
    for (auto const& p : v) 
     p->speak(); 
} 

यह आउटपुट:

Meow 
Baa 
Bark 
Baa 
Meow 
Bark 
Remove all sheep 
destruct Sheep 
destruct Sheep 
Meow 
Bark 
Meow 
Bark 
destruct Dog 
destruct Cat 
destruct Dog 
destruct Cat 

जो मेरे लिए अच्छा लग रहा है।

boost::ptr_vector<Animal> v; 
v.push_back(new Cat); 
v.push_back(new Sheep); 
v.push_back(new Dog); 
v.push_back(new Sheep); 
v.push_back(new Cat); 
v.push_back(new Dog); 
for (auto const& p : v) 
    p.speak(); 
std::cout << "Remove all sheep\n"; 
v.erase(
    std::remove_if(v.begin(), v.end(), 
        [](Animal& p) 
         {return dynamic_cast<Sheep*>(&p);}), 
    v.end()); 
for (auto const& p : v) 
    p.speak(); 

algorithm:1897:26: error: overload resolution selected deleted operator '=' 
       *__first = _VSTD::move(*__i); 
       ~~~~~~~~^~~~~~~~~~~~~~~~~~ 
test.cpp:75:9: note: in instantiation of function template specialization 'std::__1::remove_if<boost::void_ptr_iterator<std::__1::__wrap_iter<void 
     **>, Animal>, Sheep *(^)(Animal &)>' requested here 
     std::remove_if(v.begin(), v.end(), 
     ^
test.cpp:12:13: note: candidate function has been explicitly deleted 
    Animal& operator=(const Animal&) = delete; 
      ^
1 error generated. 

समस्या boost::ptr_vector की एक विशेषता है:: हालांकि मैं ptr_vector समस्याग्रस्त को यह अनुवाद पाया iterators आंतरिक रूप से संग्रहीत संकेत नहीं लौटाते हैं। वे पॉइंटर्स को संदर्भित करते हैं।और इस प्रकार जब कंटेनर std::algorithms के साथ प्रयोग किया जाता है, तो एल्गोरिदम संग्रहीत पॉइंटर्स की बजाय संग्रहीत ऑब्जेक्ट्स को प्रतिलिपि बनाने की कोशिश करता है।

एक गलती से अपनी बहुरूपी वस्तुओं गैर copyable बनाने के लिए भूल जाता है, तो उन्हें कॉपी अर्थ विज्ञान स्वचालित रूप से आपूर्ति की जाती है, एक संकलन समय त्रुटि के बजाय एक रन समय त्रुटि में परिणाम:

class Animal 
{ 
public: 
    Animal() = default; 
    virtual ~Animal() = default; 

    virtual void speak() const = 0; 
}; 

कौन सा अब इस गलत परिणाम आउटपुट:

Meow 
Baa 
Bark 
Baa 
Meow 
Bark 
Remove all sheep 
destruct Cat 
destruct Dog 
Meow 
Baa 
Bark 
Baa 
destruct Cat 
destruct Sheep 
destruct Dog 
destruct Sheep 

vector<unique_ptr> का उपयोग करते समय यह रन टाइम त्रुटि नहीं हो सकती है।

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

v.erase_if([](Animal& p) 
       {return dynamic_cast<Sheep*>(&p);}); 

आप ptr_containers के सदस्य के रूप आपूर्ति नहीं एक परिवर्तनशील अनुक्रम एल्गोरिथ्म की जरूरत है, उन तक पहुँचने के लिए करने की कोशिश नहीं करते हैं: ptr_containers के साथ इस काम करने के लिए सही तरीका केवल उन सदस्य एल्गोरिदम का उपयोग करने के लिए है <algorithm> में, या अन्य तृतीय पक्षों द्वारा आपूर्ति किए गए उन सामान्य एल्गोरिदम में।

संक्षेप में, बूस्ट :: ptr_containers ने वास्तविक आवश्यकता को भर दिया जब एकमात्र अन्य व्यावहारिक विकल्प std::vector<boost::shared_ptr<T>> था। हालांकि अब std::vector<std::unique_ptr<T>> के साथ, ओवरहेड तर्क चला गया है। और सी ++ 11 समाधान के साथ सुरक्षा और लचीलापन दोनों फायदे हैं। अगर आपको "क्लोन सेमेन्टिक्स" की ज़रूरत है, तो मैं गंभीरता से अपने clone_ptr<T> लिखने और एसडीडी कंटेनर और एल्गोरिदम के साथ इसका उपयोग करने पर विचार करता हूं।

std :: lib का पुन: उपयोग बूस्ट lib (उदा। Unordered_set/map, forward_list) की तुलना में कंटेनरों के विकल्पों को अधिक खुला रखेगा, और यह आपके विकल्पों को std :: एल्गोरिदम के रूप में यथासंभव चौड़ा रखेगा।

कहा जा रहा है कि, यदि आप काम कर रहे हैं, तो पहले से ही बूस्ट :: ptr_containers का उपयोग कर डीबग कोड, इसे बदलने की कोई जरूरी आवश्यकता नहीं है।

+0

"ओवरहेड तर्क चला गया है" - आज, मैंने वीएस2013-एक्सप्रेस के साथ कुछ परीक्षण किए और आश्चर्यजनक रूप से मुझे 'वेक्टर ' (/ ओ 2 रिलीज बिल्ड; बूस्ट 1.55) के मुकाबले 'ptr_vector' के साथ लगातार बेहतर प्रदर्शन परिणाम मिलते हैं। यह टिप्पणी एक स्पष्ट वाईएमएमवी के साथ आता है, मैंने अभी तक इसमें खोला नहीं है, लेकिन अगर कोई बहुत ही तंग जगह प्रदर्शन में है, तो यह दोनों को देखना दिलचस्प हो सकता है। –

+0

दिलचस्प, रिपोर्ट के लिए धन्यवाद। मेरे पास प्रयोग करने के लिए वीएस2013 नहीं है। क्या आकार है (unique_ptr ) == आकार (टी *) '? क्या/ओ 2 उच्चतम अनुकूलन सेटिंग है? –

+0

हाँ, आकार (uq_ptr) == आकार (टी *) == 4./ओ 2 वीएस में "गति के लिए अधिकतम" है। मुझे सूचकांक '[]' पहुंच पर ptr_vector के साथ 50% गति लाभ दिखाई देता है। –

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