Share_ptr का उपयोग करने के लिए उदाहरण?


82

नमस्ते मैंने आज एक प्रश्न पूछा कि एक ही वेक्टर सरणी में विभिन्न प्रकार की वस्तुओं को कैसे सम्मिलित किया जाए और उस प्रश्न में मेरा कोड था

 gate* G[1000];
G[0] = new ANDgate() ;
G[1] = new ORgate;
//gate is a class inherited by ANDgate and ORgate classes
class gate
{
 .....
 ......
 virtual void Run()
   {   //A virtual function
   }
};
class ANDgate :public gate 
  {.....
   .......
   void Run()
   {
    //AND version of Run
   }  

};
 class ORgate :public gate 
  {.....
   .......
   void Run()
   {
    //OR version of Run
   }  

};      
//Running the simulator using overloading concept
 for(...;...;..)
 {
  G[i]->Run() ;  //will run perfectly the right Run for the right Gate type
 } 

और मैं वैक्टर का उपयोग करना चाहता था इसलिए किसी ने लिखा कि मुझे ऐसा करना चाहिए:

std::vector<gate*> G;
G.push_back(new ANDgate); 
G.push_back(new ORgate);
for(unsigned i=0;i<G.size();++i)
{
  G[i]->Run();
}

लेकिन तब उन्होंने और कई अन्य लोगों ने सुझाव दिया कि मैं बूस्ट पॉइंटर कंटेनर
या बेहतर उपयोग करूंगा shared_ptr। मैंने इस विषय के बारे में पढ़ने में पिछले 3 घंटे बिताए हैं, लेकिन दस्तावेज मुझे बहुत उन्नत लगते हैं। **** क्या कोई मुझे shared_ptrउपयोग का एक छोटा कोड उदाहरण दे सकता है और उन्होंने इसका उपयोग करने का सुझाव क्यों दिया shared_ptr। इसके अलावा अन्य प्रकार भी हैं ptr_vector, ptr_listऔर ptr_deque** **

Edit1: मैंने एक कोड उदाहरण भी पढ़ा है जिसमें शामिल हैं:

typedef boost::shared_ptr<Foo> FooPtr;
.......
int main()
{
  std::vector<FooPtr>         foo_vector;
........
FooPtr foo_ptr( new Foo( 2 ) );
  foo_vector.push_back( foo_ptr );
...........
}

और मैं वाक्यविन्यास नहीं समझता!


2
आप कौन सा वाक्यविन्यास नहीं समझते हैं? mainवेक्टर बनाने की पहली पंक्ति जिसमें एक प्रकार का साझा करने के लिए साझा संकेत हो सकते हैं Foo; दूसरा व्यक्ति इसका Fooउपयोग करने के लिए new, और इसे साझा करने के लिए एक साझा सूचक बनाता है; तीसरा वेक्टर में साझा सूचक की एक प्रति डालता है।
माइक सेमोर

जवाबों:


116

एक का उपयोग vectorके shared_ptrहटा स्मृति लीक क्योंकि आप वेक्टर और कॉल चलने के लिए भूल गया की संभावना deleteप्रत्येक तत्व पर। आइए लाइन-बाय-लाइन के थोड़ा संशोधित संस्करण के माध्यम से चलते हैं।

typedef boost::shared_ptr<gate> gate_ptr;

साझा सूचक प्रकार के लिए एक उपनाम बनाएं। यह C ++ भाषा में कुरूपता से बचा जाता है जो टाइपिंग से अधिक होने वाले संकेतों केstd::vector<boost::shared_ptr<gate> > बीच की जगह को टाइप करने और भूलने के परिणामस्वरूप होता है ।

    std::vector<gate_ptr> vec;

boost::shared_ptr<gate>वस्तुओं का खाली वेक्टर बनाता है ।

    gate_ptr ptr(new ANDgate);

एक नया ANDgateउदाहरण आवंटित करें और इसे एक में संग्रहीत करें shared_ptr। अलग से ऐसा करने का कारण एक समस्या को रोकना है जो एक ऑपरेशन फेंकता है। इस उदाहरण में यह संभव नहीं है। बूस्ट shared_ptr"सर्वोत्तम प्रथाओं" स्पष्टीकरण दें कि यह एक है सबसे अच्छा अभ्यास के लिए एक अस्थायी के बजाय एक नि: शुल्क से चली आ रही वस्तु में आवंटित करने के लिए।

    vec.push_back(ptr);

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

जो नहीं समझाया गया है, वह यह है कि विध्वंसक shared_ptr<gate>यह सुनिश्चित करता है कि आवंटित मेमोरी हटा दी गई है। यहीं से मेमोरी लीकेज से बचा जाता है। विध्वंसक std::vector<T>यह सुनिश्चित करता है कि Tवेक्टर में संग्रहीत प्रत्येक तत्व के लिए विध्वंसक को कहा जाता है। हालाँकि, एक पॉइंटर के लिए डिस्ट्रक्टर (उदाहरण के लिए gate*) आपके द्वारा आवंटित की गई मेमोरी को डिलीट नहीं करता है । यही आप का उपयोग करके shared_ptrया से बचने की कोशिश कर रहे हैं ptr_vector


1
वह विस्तृत था :)। मेरा प्रश्न कोड गेट की 3 लाइन के बारे में है गेट_प्ट्र पीटीआर (नया एंडगेट); यह मेरे लिए बहुत परिचित महसूस नहीं करता है, एक प्रकार का साझा सूचक गेट का ptr और फिर ब्रेसिज़ के बीच आपने एक नया एंडगेट भेजा! वह भ्रामक है।
अहमद

6
@ अहम्: समग्र अभिव्यक्ति एक वैरिएबल इनिशियलाइज़ेशन है, जैसे आप वैल्यू के साथ int x(5);इनिशियलाइज़ करने के लिए लिख सकते हैं x। 5. इस मामले में, इसे नए-एक्सप्रेशन के मूल्य से इनिशियलाइज़ किया जा रहा है, जो एक बनाता है ANDgate; नई-अभिव्यक्ति का मूल्य नई वस्तु का सूचक है।
माइक सेमुर

42

मैं जोड़ देगा के बारे में महत्वपूर्ण बातें की है कि एक shared_ptrके लिए ही है कभी उन्हें निम्न सिंटैक्स के साथ निर्माण:

shared_ptr<Type>(new Type(...));

इस तरह, "वास्तविक" पॉइंटर Typeआपके दायरे के लिए अनाम है, और केवल साझा पॉइंटर द्वारा आयोजित किया जाता है। इस प्रकार यह आपके लिए गलती से इस "वास्तविक" पॉइंटर का उपयोग करना असंभव होगा। दूसरे शब्दों में, ऐसा कभी न करें:

Type* t_ptr = new Type(...);
shared_ptr<Type> t_sptr ptrT(t_ptr);
//t_ptr is still hanging around!  Don't use it!

यद्यपि यह काम करेगा, अब आपके पास अपने फ़ंक्शन में एक Type*पॉइंटर ( t_ptr) है जो साझा पॉइंटर के बाहर रहता है। यह t_ptrकहीं भी उपयोग करने के लिए खतरनाक है , क्योंकि आप कभी नहीं जानते कि साझा सूचक जो इसे धारण करता है वह इसे नष्ट कर सकता है, और आप सेगफॉल्ट करेंगे।

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


8
केन ने जो कुछ भी कहा वह अच्छा और सत्य है, लेकिन मेरा मानना ​​है कि इसे कॉल करने का पसंदीदा तरीका अब है auto t_ptr = make_shared<Type>(...);या समकक्ष है shared_ptr<Type> t_ptr = make_shared<Type>(...);, केवल इसलिए कि यह फॉर्म अधिक कुशल है।
जेसन सिड्स

@KenSimon, वहाँ और के ,बीच एक अल्पविराम माना जाता है ? t_sptrptrTshared_ptr<Type> t_sptr ptrT(t_ptr);
एलनकुंजी

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

20

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

एक मुद्दा जो उठता है वह यह है कि अपवादों के साथ यह सुनिश्चित करना बहुत कठिन हो सकता है कि किसी वस्तु को हमेशा सभी संभव निष्पादन मार्गों में एक बार जारी किया जाए।

यह RAII का कारण है: http://en.wikipedia.org/wiki/RAII

यह सुनिश्चित करने के उद्देश्य से एक सहायक वर्ग बनाना कि सभी निष्पादन पथों में एक वस्तु हमेशा एक बार हटा दी जाए।

इस तरह एक वर्ग का उदाहरण है: std :: auto_ptr

लेकिन कभी-कभी आप वस्तुओं को दूसरे के साथ साझा करना पसंद करते हैं। इसे केवल तभी हटाया जाना चाहिए जब कोई भी इसका उपयोग न करे।

उस संदर्भ के साथ मदद करने के लिए मतगणना की रणनीति विकसित की गई है, लेकिन आपको अभी भी अडंर को याद रखने और मैन्युअल रूप से रेफरी जारी करने की आवश्यकता है। संक्षेप में यह नई / हटाने जैसी ही समस्या है।

इसीलिए बढ़ावा देने से बढ़ावा मिला है :: share_ptr, यह संदर्भ गिनती स्मार्ट पॉइंटर है ताकि आप वस्तुओं को साझा कर सकें और स्मृति को अनजाने में लीक न कर सकें।

C ++ tr1 के जोड़ के साथ यह अब c ++ मानक के साथ भी जोड़ा जाता है, लेकिन इसका नाम std :: tr1 :: shared_ptr <> है।

यदि संभव हो तो मैं मानक साझा सूचक का उपयोग करने की सलाह देता हूं। ptr_list, ptr_dequeue और इसलिए सूचक प्रकारों के लिए IIRC विशेष कंटेनर हैं। मैं उन्हें अभी के लिए अनदेखा करता हूं।

तो हम आपके उदाहरण से शुरू कर सकते हैं:

std::vector<gate*> G; 
G.push_back(new ANDgate);  
G.push_back(new ORgate); 
for(unsigned i=0;i<G.size();++i) 
{ 
  G[i]->Run(); 
} 

अब यहाँ समस्या यह है कि जब भी G दायरा निकलता है तो हम 2 वस्तुओं को जोड़ते हैं। जी को जोड़ते हैं। इसे std का उपयोग करने के लिए फिर से लिखते हैं :: tr1 :: share_ptr

// Remember to include <memory> for shared_ptr
// First do an alias for std::tr1::shared_ptr<gate> so we don't have to 
// type that in every place. Call it gate_ptr. This is what typedef does.
typedef std::tr1::shared_ptr<gate> gate_ptr;    
// gate_ptr is now our "smart" pointer. So let's make a vector out of it.
std::vector<gate_ptr> G; 
// these smart_ptrs can't be implicitly created from gate* we have to be explicit about it
// gate_ptr (new ANDgate), it's a good thing:
G.push_back(gate_ptr (new ANDgate));  
G.push_back(gate_ptr (new ORgate)); 
for(unsigned i=0;i<G.size();++i) 
{ 
   G[i]->Run(); 
} 

जब G स्कोप से बाहर हो जाता है तो मेमोरी अपने आप रिकवर हो जाती है।

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


मेरे प्रशिक्षक ने मुझे अपनी खुद की कक्षाएं लिखने के बारे में एक समान सलाह दी, इसलिए मैं इसे सुनिश्चित करने की कोशिश करूंगा। टीआई।
अहमद

आपको सभी गेट्स को चलाने के लिए एक इटरेटर का उपयोग करना चाहिएfor( auto itt = G.begin(); itt != G.end(); ++itt ){ itt->Run(); }
गिलुम मास

1
या बेहतर अभी तक C ++ में नया "foreach"
एक और रूपक

2

बूस्ट डॉक्यूमेंटेशन बहुत अच्छा उदाहरण देता है: शेयर्ड_एप्ट्र उदाहरण (यह वास्तव में स्मार्ट पॉइंटर्स के वेक्टर के बारे में है) या शेयर्ड_प्ट्र डॉक जोहान्स स्काउब द्वारा निम्नलिखित उत्तर बूस्ट स्मार्ट पॉइंटर्स को बहुत अच्छी तरह से समझाता है : स्मार्ट पॉइंटर्स समझाया

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

 // if you just do
 G.clear() // will clear the vector but you'll be left with 2 memory leaks
 ...
// to properly clean the vector and the objects behind it
for (std::vector<gate*>::iterator it = G.begin(); it != G.end(); it++)
{
  delete (*it);
}

बढ़ावा देना :: ptr_vector <> आपके लिए उपरोक्त को संभाल लेगा - इसका अर्थ है कि यह स्टोर करने वाले पॉइंटर्स के पीछे मेमोरी को हटा देगा।


share_ptr एक स्मार्ट पॉइंटर है - एक सादे पॉइंटर के लिए एक चमकदार "रैपर" जो कहता है कि एक पॉइंटर प्रकार में कुछ AI जोड़ता है। ptr_vector पॉइंटर्स के लिए एक स्मार्ट कंटेनर है - पॉइंटर्स के कंटेनर के लिए एक "आवरण"।
celavek

तो ptr_vector सामान्य वेक्टर के प्रतिस्थापन का एक प्रकार है?
अहमद

@ मुझे लगता है कि आप ऐसा सोच सकते हैं।
celavek

2

बूस्ट के माध्यम से आप इसे कर सकते हैं>

std::vector<boost::any> vecobj;
    boost::shared_ptr<string> sharedString1(new string("abcdxyz!"));    
    boost::shared_ptr<int> sharedint1(new int(10));
    vecobj.push_back(sharedString1);
    vecobj.push_back(sharedint1);

> अपने वेक्टर कंटेनर में विभिन्न ऑब्जेक्ट प्रकार डालने के लिए। एक्सेस करने के लिए आपको किसी भी_का उपयोग करना होगा, जो डायनेमिक_कास्ट की तरह काम करता है, आशा है कि यह आपकी जरूरत के लिए काम करेगा।


1
#include <memory>
#include <iostream>

class SharedMemory {
    public: 
        SharedMemory(int* x):_capture(x){}
        int* get() { return (_capture.get()); }
    protected:
        std::shared_ptr<int> _capture;
};

int main(int , char**){
    SharedMemory *_obj1= new SharedMemory(new int(10));
    SharedMemory *_obj2 = new SharedMemory(*_obj1);
    std::cout << " _obj1: " << *_obj1->get() << " _obj2: " << *_obj2->get()
    << std::endl;
    delete _obj2;

    std::cout << " _obj1: " << *_obj1->get() << std::endl;
    delete _obj1;
    std::cout << " done " << std::endl;
}

यह क्रिया में shared_ptr का एक उदाहरण है। _obj2 हटा दिया गया था लेकिन सूचक अभी भी मान्य है। आउटपुट है ।/test _obj1: 10 _obj2: 10 _obj2: 10 किया


0

एक ही कंटेनर में विभिन्न वस्तुओं को जोड़ने का सबसे अच्छा तरीका है मेक_शेयर, वेक्टर और रेंज आधारित लूप का उपयोग करना और आपके पास एक अच्छा, साफ और "पठनीय" कोड होगा!

typedef std::shared_ptr<gate> Ptr   
vector<Ptr> myConatiner; 
auto andGate = std::make_shared<ANDgate>();
myConatiner.push_back(andGate );
auto orGate= std::make_shared<ORgate>();
myConatiner.push_back(orGate);

for (auto& element : myConatiner)
    element->run();
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.