जवाबों:
एक अच्छा उदाहरण कैश होगा।
हाल ही में एक्सेस किए गए ऑब्जेक्ट्स के लिए, आप उन्हें मेमोरी में रखना चाहते हैं, इसलिए आप उनके लिए एक मजबूत पॉइंटर रखें। समय-समय पर, आप कैश को स्कैन करते हैं और तय करते हैं कि हाल ही में किन वस्तुओं को एक्सेस नहीं किया गया है। आपको उन यादों को रखने की आवश्यकता नहीं है, इसलिए आपको मजबूत पॉइंटर से छुटकारा मिलता है।
लेकिन क्या होगा अगर वह वस्तु उपयोग में है और कोई अन्य कोड उसके लिए एक मजबूत संकेतक रखता है? यदि कैश को ऑब्जेक्ट के अपने एकमात्र पॉइंटर से छुटकारा मिल जाता है, तो वह इसे फिर से कभी नहीं पा सकता है। तो कैश उन वस्तुओं के लिए एक कमजोर संकेतक रखता है जिन्हें यह खोजने की आवश्यकता है कि क्या वे स्मृति में रहने के लिए होते हैं।
यह ठीक वही है जो एक कमजोर सूचक करता है - यह आपको एक वस्तु का पता लगाने की अनुमति देता है यदि यह अभी भी आस-पास है, लेकिन अगर इसे और कुछ नहीं चाहिए तो इसे आसपास नहीं रखें।
std::weak_ptr
लटकती सूचक समस्या को हल करने का एक बहुत अच्छा तरीका है। केवल कच्चे पॉइंटर्स का उपयोग करके यह जानना असंभव है कि संदर्भित डेटा डील किया गया है या नहीं। इसके बजाय, std::shared_ptr
डेटा का प्रबंधन करने और डेटा के std::weak_ptr
उपयोगकर्ताओं को आपूर्ति करने से, उपयोगकर्ता कॉल करके expired()
या डेटा की वैधता की जांच कर सकते हैं lock()
।
आप std::shared_ptr
अकेले ऐसा नहीं कर सकते थे, क्योंकि सभी std::shared_ptr
उदाहरण डेटा के स्वामित्व को साझा करते हैं जो हटाए जाने के सभी उदाहरणों से पहले हटाए नहीं std::shared_ptr
जाते हैं। यहाँ एक उदाहरण है कि कैसे झूलने वाले पॉइंटर का उपयोग करके जांच करें lock()
:
#include <iostream>
#include <memory>
int main()
{
// OLD, problem with dangling pointer
// PROBLEM: ref will point to undefined data!
int* ptr = new int(10);
int* ref = ptr;
delete ptr;
// NEW
// SOLUTION: check expired() or lock() to determine if pointer is valid
// empty definition
std::shared_ptr<int> sptr;
// takes ownership of pointer
sptr.reset(new int);
*sptr = 10;
// get pointer to data without taking ownership
std::weak_ptr<int> weak1 = sptr;
// deletes managed object, acquires new pointer
sptr.reset(new int);
*sptr = 5;
// get pointer to new data without taking ownership
std::weak_ptr<int> weak2 = sptr;
// weak1 is expired!
if(auto tmp = weak1.lock())
std::cout << *tmp << '\n';
else
std::cout << "weak1 is expired\n";
// weak2 points to new data (5)
if(auto tmp = weak2.lock())
std::cout << *tmp << '\n';
else
std::cout << "weak2 is expired\n";
}
std::weak_ptr::lock
एक नया बनाता है std::shared_ptr
जो प्रबंधित ऑब्जेक्ट के स्वामित्व को साझा करता है।
एक और जवाब, उम्मीद है कि सरल। (साथी गोगलर्स के लिए)
मान लीजिए आपके पास Team
और Member
वस्तुएं हैं।
जाहिर है कि यह एक संबंध है: Team
वस्तु को इसके संकेत मिलेंगे Members
। और यह संभावना है कि सदस्यों को उनकी Team
वस्तु का बैक पॉइंटर भी होगा ।
तब आपके पास एक निर्भरता चक्र होता है। यदि आप उपयोग करते हैं shared_ptr
, तो वस्तुओं को स्वचालित रूप से मुक्त नहीं किया जाएगा जब आप उन पर संदर्भ छोड़ देते हैं, क्योंकि वे एक दूसरे को चक्रीय तरीके से संदर्भित करते हैं। यह एक मेमोरी लीक है।
आप उपयोग करके इसे तोड़ते हैं weak_ptr
। "स्वामी" आमतौर पर उपयोग करते हैं shared_ptr
और "स्वामित्व" weak_ptr
अपने माता-पिता के लिए उपयोग करते हैं , और इसे अस्थायी रूप से shared_ptr
तब परिवर्तित करते हैं जब इसे अपने माता-पिता तक पहुंच की आवश्यकता होती है।
एक कमजोर ptr स्टोर करें:
weak_ptr<Parent> parentWeakPtr_ = parentSharedPtr; // automatic conversion to weak from shared
फिर जरूरत पड़ने पर इसका इस्तेमाल करें
shared_ptr<Parent> tempParentSharedPtr = parentWeakPtr_.lock(); // on the stack, from the weak ptr
if( !tempParentSharedPtr ) {
// yes, it may fail if the parent was freed since we stored weak_ptr
} else {
// do stuff
}
// tempParentSharedPtr is released when it goes out of scope
shared_ptr
स्वामित्व को साझा करना है, इसलिए किसी के पास स्मृति को मुक्त करने की कोई विशेष जिम्मेदारी नहीं है, यह स्वचालित रूप से मुक्त हो जाता है जब इसका उपयोग नहीं किया जाता है। जब तक कोई पाश नहीं है ... आपके पास एक ही खिलाड़ी (पिछली टीमों?) को साझा करने वाली कई टीमें हो सकती हैं। यदि टीम सदस्यों को "मालिक" करती है, तो shared_ptr
शुरू करने के लिए ए का उपयोग करने की कोई आवश्यकता नहीं है ।
shared_ptr
अपने "टीम के सदस्यों" द्वारा संदर्भित है, यह कब नष्ट हो जाएगी? आप जो वर्णन कर रहे हैं वह एक ऐसा मामला है जहां कोई लूप नहीं है।
यहाँ एक उदाहरण दिया गया है, @jleahy द्वारा मुझे दिया गया: मान लीजिए कि आपके पास कार्यों का एक संग्रह है, जिसे एसिंक्रोनस रूप से निष्पादित किया गया है, और एक में प्रबंधित किया गया है std::shared_ptr<Task>
। आप समय-समय पर उन कार्यों के साथ कुछ करना चाहते हो सकते हैं, इसलिए एक टाइमर घटना हो सकती है std::vector<std::weak_ptr<Task>>
और कार्यों को कुछ करने के लिए दे सकती है। हालाँकि, एक साथ एक कार्य ने समवर्ती रूप से निर्णय लिया हो सकता है कि अब इसकी आवश्यकता नहीं है और मर जाते हैं। टाइमर इस प्रकार जाँच सकता है कि क्या कार्य अभी भी कमजोर पॉइंटर से एक साझा पॉइंटर बनाकर और उस साझा पॉइंटर का उपयोग करके जीवित है, बशर्ते यह अशक्त न हो।
वे Boost.Asio के साथ उपयोगी होते हैं जब आपको गारंटी नहीं दी जाती है कि एक एसिंक्रोनस हैंडलर के आह्वान पर लक्ष्य वस्तु अभी भी मौजूद है। चाल को weak_ptr
असंबद्ध हैंडलर ऑब्जेक्ट में बांधना है , लैम्बडा std::bind
कैप्चर का उपयोग करना है ।
void MyClass::startTimer()
{
std::weak_ptr<MyClass> weak = shared_from_this();
timer_.async_wait( [weak](const boost::system::error_code& ec)
{
auto self = weak.lock();
if (self)
{
self->handleTimeout();
}
else
{
std::cout << "Target object no longer exists!\n";
}
} );
}
यह self = shared_from_this()
Boost.Asio उदाहरणों में अक्सर देखे जाने वाले मुहावरों का एक प्रकार है , जहां एक लंबित अतुल्यकालिक हैंडलर लक्ष्य ऑब्जेक्ट के जीवनकाल को लम्बा नहीं करेगा , फिर भी लक्ष्य ऑब्जेक्ट हटाए जाने पर अभी भी सुरक्षित है।
this
self = shared_from_this()
मुहावरे का उपयोग करते समय आदत का बल जब हैंडलर उसी कक्षा के भीतर तरीकों का आह्वान करता है।
share_ptr : असली वस्तु रखता है।
weak_ptr : lock
वास्तविक मालिक से कनेक्ट करने के लिए उपयोग करता है या shared_ptr
अन्यथा NULL देता है ।
मोटे तौर पर, weak_ptr
भूमिका हाउसिंग एजेंसी की भूमिका के समान है । एजेंटों के बिना, किराए पर घर लेने के लिए हमें शहर में यादृच्छिक घरों की जांच करनी पड़ सकती है। एजेंट यह सुनिश्चित करते हैं कि हम केवल उन घरों का दौरा करें जो अभी भी सुलभ हैं और किराए पर उपलब्ध हैं ।
weak_ptr
किसी वस्तु के सही विलोपन की जांच करना भी अच्छा है - विशेषकर इकाई परीक्षणों में। विशिष्ट उपयोग मामला इस तरह दिख सकता है:
std::weak_ptr<X> weak_x{ shared_x };
shared_x.reset();
BOOST_CHECK(weak_x.lock());
... //do something that should remove all other copies of shared_x and hence destroy x
BOOST_CHECK(!weak_x.lock());
पॉइंटर्स का उपयोग करते समय उपलब्ध विभिन्न प्रकार के पॉइंटर्स को समझना महत्वपूर्ण है और जब यह प्रत्येक को उपयोग करने के लिए समझ में आता है। दो श्रेणियों में चार प्रकार के संकेत निम्न हैं:
SomeClass* ptrToSomeClass = new SomeClass();
]std::unique_ptr<SomeClass> uniquePtrToSomeClass ( new SomeClass() );
std::shared_ptr<SomeClass> sharedPtrToSomeClass ( new SomeClass() );
std::weak_ptr<SomeClass> weakPtrToSomeWeakOrSharedPtr ( weakOrSharedPtr );
रॉ पॉइंटर्स (कभी-कभी "लीगेसी पॉइंटर्स", या "सी पॉइंटर्स" के रूप में संदर्भित) 'नंगे-हड्डियों' सूचक व्यवहार प्रदान करते हैं और बग और मेमोरी लीक का एक सामान्य स्रोत हैं। कच्चे पॉइंटर्स संसाधन के स्वामित्व पर नज़र रखने के लिए कोई साधन प्रदान नहीं करते हैं और डेवलपर्स को यह सुनिश्चित करने के लिए मैन्युअल रूप से 'डिलीट' करना होगा कि वे स्मृति रिसाव नहीं बना रहे हैं। यह मुश्किल हो जाता है यदि संसाधन साझा किया जाता है क्योंकि यह जानना चुनौतीपूर्ण हो सकता है कि क्या कोई वस्तु अभी भी संसाधन की ओर इशारा कर रही है। इन कारणों से, कच्चे पॉइंटर्स को आम तौर पर टाला जाना चाहिए और केवल सीमित दायरे वाले कोड के प्रदर्शन-महत्वपूर्ण खंडों में उपयोग किया जाता है।
यूनीक पॉइंटर्स एक बुनियादी स्मार्ट पॉइंटर हैं जो संसाधन के अंतर्निहित कच्चे पॉइंटर का 'मालिक' है और डिलीट की गई कॉल को डिलीट करने और आवंटित मेमोरी को फ्रीज करने के लिए जिम्मेदार है, जो यूनीक पॉइंटर के दायरे से बाहर जाने वाली वस्तु है। 'यूनिक' नाम का तात्पर्य इस बात से है कि किसी वस्तु का केवल एक निश्चित समय में विशिष्ट संकेतक 'स्वयं' हो सकता है। चाल कमांड के माध्यम से स्वामित्व को किसी अन्य ऑब्जेक्ट में स्थानांतरित किया जा सकता है, लेकिन एक अद्वितीय सूचक को कभी भी कॉपी या साझा नहीं किया जा सकता है। इन कारणों के लिए, यूनिक पॉइंटर्स कच्चे पॉइंटर्स के लिए एक अच्छा विकल्प है, इस मामले में कि किसी एक वस्तु को एक निश्चित समय पर पॉइंटर की आवश्यकता होती है, और यह डेवलपर को आवर्ती वस्तु के जीवनचक्र के अंत में मेमोरी को मुक्त करने की आवश्यकता को कम करता है।
साझा पॉइंटर्स एक अन्य प्रकार के स्मार्ट पॉइंटर हैं जो अद्वितीय पॉइंटर्स के समान हैं, लेकिन कई ऑब्जेक्ट्स को साझा पॉइंटर पर स्वामित्व रखने की अनुमति देते हैं। यूनीक पॉइंटर की तरह, साझा पॉइंटर्स आवंटित मेमोरी को मुक्त करने के लिए जिम्मेदार होते हैं, जब सभी ऑब्जेक्ट संसाधन की ओर इशारा करते हैं। यह इसे संदर्भ गिनती नामक तकनीक के साथ पूरा करता है। हर बार जब कोई नया ऑब्जेक्ट साझा किए गए पॉइंटर का स्वामित्व लेता है तो संदर्भ गणना एक से बढ़ जाती है। इसी तरह, जब कोई वस्तु दायरे से बाहर जाती है या संसाधन की ओर इशारा करती है, तो संदर्भ संख्या एक से घट जाती है। जब संदर्भ संख्या शून्य तक पहुंच जाती है, तो आवंटित मेमोरी मुक्त हो जाती है। इन कारणों के लिए, साझा पॉइंटर्स एक बहुत ही शक्तिशाली प्रकार के स्मार्ट पॉइंटर हैं जिनका उपयोग कभी भी किया जाना चाहिए कई वस्तुओं को एक ही संसाधन को इंगित करने की आवश्यकता होती है।
अंत में, कमजोर संकेत एक अन्य प्रकार के स्मार्ट पॉइंटर हैं, जो सीधे किसी संसाधन की ओर इशारा करने के बजाय, वे दूसरे पॉइंटर (कमजोर या साझा) की ओर इशारा करते हैं। कमजोर पॉइंटर्स किसी ऑब्जेक्ट को सीधे एक्सेस नहीं कर सकते हैं, लेकिन वे बता सकते हैं कि क्या ऑब्जेक्ट अभी भी मौजूद है या यदि यह समाप्त हो गया है। एक कमजोर पॉइंटर को पॉइंट-टू ऑब्जेक्ट (इसे अभी भी मौजूद है) तक पहुंचने के लिए अस्थायी रूप से एक साझा पॉइंटर में परिवर्तित किया जा सकता है। उदाहरण के लिए, निम्नलिखित उदाहरण पर विचार करें:
उदाहरण में, आपके पास मीटिंग बी के लिए एक कमजोर संकेतक है। मीटिंग बी में आप "मालिक" नहीं हैं, इसलिए यह आपके बिना समाप्त हो सकता है, और आपको नहीं पता कि यह समाप्त हुआ या नहीं जब तक आप जांच नहीं करते। यदि यह समाप्त नहीं हुआ है, तो आप इसमें शामिल हो सकते हैं और भाग ले सकते हैं, अन्यथा, आप नहीं कर सकते। यह मीटिंग B के लिए एक साझा सूचक होने से भिन्न है क्योंकि मीटिंग मीटिंग A और मीटिंग B (एक ही समय में दोनों में भाग लेना) दोनों में आप "स्वामी" होंगे।
उदाहरण दिखाता है कि एक कमजोर सूचक कैसे काम करता है और उपयोगी है जब किसी वस्तु को बाहरी पर्यवेक्षक होने की आवश्यकता होती है , लेकिन स्वामित्व साझा करने की जिम्मेदारी नहीं चाहता है। यह इस परिदृश्य में विशेष रूप से उपयोगी है कि दो वस्तुओं को एक दूसरे को इंगित करने की आवश्यकता है (उर्फ एक परिपत्र संदर्भ)। साझा पॉइंटर्स के साथ, न तो ऑब्जेक्ट को जारी किया जा सकता है क्योंकि वे अभी भी अन्य ऑब्जेक्ट द्वारा इंगित 'दृढ़ता से' हैं। जब बिंदु में से एक कमजोर सूचक होता है, तो कमजोर सूचक रखने वाली वस्तु जरूरत पड़ने पर भी दूसरी वस्तु तक पहुंच सकती है, बशर्ते वह अभी भी मौजूद हो।
अन्य पहले से ही मान्य उपयोग के मामलों के अलावा std::weak_ptr
एक बहुआयामी वातावरण में एक भयानक उपकरण है, क्योंकि
std::shared_ptr
के साथ संयोजन के रूप में std::weak_ptr
झूलने बिंदुओं के खिलाफ सुरक्षित है - std::unique_ptr
कच्चे संकेत के साथ संयोजन के विपरीतstd::weak_ptr::lock()
एक परमाणु ऑपरेशन है (यह भी देखें) कमजोरों के बारे में थ्रेड-सुरक्षा के बारे में )एक निर्देशिका की सभी छवियों (~ 10.000) को एक साथ मेमोरी में लोड करने के लिए एक कार्य पर विचार करें (उदाहरण के लिए थंबनेल कैश)। जाहिर है ऐसा करने का सबसे अच्छा तरीका एक नियंत्रण धागा है, जो छवियों को संभालता है और प्रबंधित करता है, और कई कार्यकर्ता धागे, जो छवियों को लोड करते हैं। अब यह एक आसान काम है। यहाँ एक बहुत ही सरल कार्यान्वयन है (join()
आदि को छोड़ दिया गया है, थ्रेड्स को वास्तविक कार्यान्वयन आदि में अलग तरीके से संभाला जाना होगा)
// a simplified class to hold the thumbnail and data
struct ImageData {
std::string path;
std::unique_ptr<YourFavoriteImageLibData> image;
};
// a simplified reader fn
void read( std::vector<std::shared_ptr<ImageData>> imagesToLoad ) {
for( auto& imageData : imagesToLoad )
imageData->image = YourFavoriteImageLib::load( imageData->path );
}
// a simplified manager
class Manager {
std::vector<std::shared_ptr<ImageData>> m_imageDatas;
std::vector<std::unique_ptr<std::thread>> m_threads;
public:
void load( const std::string& folderPath ) {
std::vector<std::string> imagePaths = readFolder( folderPath );
m_imageDatas = createImageDatas( imagePaths );
const unsigned numThreads = std::thread::hardware_concurrency();
std::vector<std::vector<std::shared_ptr<ImageData>>> splitDatas =
splitImageDatas( m_imageDatas, numThreads );
for( auto& dataRangeToLoad : splitDatas )
m_threads.push_back( std::make_unique<std::thread>(read, dataRangeToLoad) );
}
};
लेकिन यह बहुत अधिक जटिल हो जाता है, यदि आप छवियों के लोडिंग को बाधित करना चाहते हैं, उदाहरण के लिए क्योंकि उपयोगकर्ता ने एक अलग निर्देशिका चुनी है। या यहां तक कि अगर आप प्रबंधक को नष्ट करना चाहते हैं।
इससे पहले कि आप अपना बदलाव कर सकें, आपको थ्रेड कम्युनिकेशन की आवश्यकता होगी और सभी लोडर थ्रेड्स को रोकना होगा m_imageDatas
क्षेत्र । अन्यथा लोडर तब तक लोड करते रहेंगे जब तक कि सभी चित्र नहीं हो जाते - भले ही वे पहले से ही अप्रचलित हों। सरलीकृत उदाहरण में, यह बहुत कठिन नहीं होगा, लेकिन वास्तविक वातावरण में चीजें बहुत अधिक जटिल हो सकती हैं।
धागे संभवतः कई प्रबंधकों द्वारा उपयोग किए जाने वाले थ्रेड पूल का हिस्सा होंगे, जिनमें से कुछ को रोका जा रहा है, और कुछ नहीं हैं आदि। सरल पैरामीटर imagesToLoad
एक बंद कतार होगी, जिसमें वे प्रबंधक अलग-अलग नियंत्रण थ्रेड्स से अपनी छवि अनुरोधों को धक्का देते हैं। पाठकों को अनुरोधों के साथ - एक अनियंत्रित क्रम में - दूसरे छोर पर। और इसलिए संचार कठिन, धीमा और त्रुटि रहित हो जाता है। ऐसे मामलों में किसी भी अतिरिक्त संचार से बचने के लिए एक बहुत ही सुंदर तरीका है, std::shared_ptr
जिसके साथ संयोजन के रूप में उपयोग करना है std::weak_ptr
।
// a simplified reader fn
void read( std::vector<std::weak_ptr<ImageData>> imagesToLoad ) {
for( auto& imageDataWeak : imagesToLoad ) {
std::shared_ptr<ImageData> imageData = imageDataWeak.lock();
if( !imageData )
continue;
imageData->image = YourFavoriteImageLib::load( imageData->path );
}
}
// a simplified manager
class Manager {
std::vector<std::shared_ptr<ImageData>> m_imageDatas;
std::vector<std::unique_ptr<std::thread>> m_threads;
public:
void load( const std::string& folderPath ) {
std::vector<std::string> imagePaths = readFolder( folderPath );
m_imageDatas = createImageDatas( imagePaths );
const unsigned numThreads = std::thread::hardware_concurrency();
std::vector<std::vector<std::weak_ptr<ImageData>>> splitDatas =
splitImageDatasToWeak( m_imageDatas, numThreads );
for( auto& dataRangeToLoad : splitDatas )
m_threads.push_back( std::make_unique<std::thread>(read, dataRangeToLoad) );
}
};
यह कार्यान्वयन लगभग पहले के रूप में आसान है, किसी भी अतिरिक्त थ्रेड संचार की आवश्यकता नहीं है, और एक वास्तविक कार्यान्वयन में थ्रेड पूल / कतार का हिस्सा हो सकता है। चूंकि एक्सपायर्ड इमेजेज को छोड़ दिया जाता है, और नॉन-एक्सपायर्ड इमेजेस को प्रोसेस किया जाता है, इसलिए थ्रेड्स को कभी भी नॉर्मल ऑपरेशन के दौरान बंद नहीं करना पड़ेगा। यदि पाठक के पास सूचक समाप्त नहीं हुआ है, तो आप हमेशा सुरक्षित रूप से पथ बदल सकते हैं या अपने प्रबंधकों को नष्ट कर सकते हैं।
http://en.cppreference.com/w/cpp/memory/weak_ptr std :: weak_ptr एक स्मार्ट पॉइंटर है जो एक ऑब्जेक्ट के लिए एक गैर-स्वामित्व ("कमजोर") संदर्भ रखता है जिसे std द्वारा प्रबंधित किया जाता है - Share_ptr। संदर्भित ऑब्जेक्ट तक पहुँचने के लिए इसे std :: shared_ptr में परिवर्तित किया जाना चाहिए।
std :: weak_ptr मॉडल अस्थायी स्वामित्व: जब किसी वस्तु को केवल तभी मौजूद होना चाहिए, जब वह मौजूद हो, और उसे किसी अन्य व्यक्ति द्वारा किसी भी समय हटा दिया जा सकता है, तो std :: weak_ptr का उपयोग वस्तु को ट्रैक करने के लिए किया जाता है, और इसे std में बदल दिया जाता है: : साझा स्वामित्व को अस्थायी स्वामित्व मानने के लिए। यदि मूल std :: साझा_ptr इस समय नष्ट हो जाता है, तो वस्तु का जीवनकाल तब तक बढ़ाया जाता है जब तक कि अस्थायी std :: साझा_ptr नष्ट नहीं हो जाता।
इसके अलावा, std :: weak_ptr का उपयोग std के सर्कुलर रेफरेंस को तोड़ने के लिए किया जाता है :: share_ptr।
साझा सूचक की एक खामी है: साझा_पॉइंट पैरेंट-चाइल्ड चक्र निर्भरता को संभाल नहीं सकता है। यदि माता-पिता वर्ग साझा पॉइंटर का उपयोग करके चाइल्ड क्लास के ऑब्जेक्ट का उपयोग करता है, तो उसी फाइल में यदि चाइल्ड क्लास पैरेंट क्लास के ऑब्जेक्ट का उपयोग करता है। साझा सूचक सभी वस्तुओं को नष्ट करने में विफल हो जाएगा, यहां तक कि साझा सूचक चक्र निर्भरता परिदृश्य में विध्वंसक को कॉल करने में बिल्कुल भी नहीं है। मूल रूप से साझा किए गए पॉइंटर संदर्भ गणना तंत्र का समर्थन नहीं करते हैं।
यह कमी हम कमजोर_पॉइंटर्स का उपयोग करके दूर कर सकते हैं।
weak_ptr
ड्रॉप में प्रतिस्थापन के रूप में प्रोग्राम लॉजिक में कोई बदलाव नहीं होने के साथ एक परिपत्र निर्भरता के साथ एक सौदा कैसे हो सकता है shared_ptr
?" :-)
जब हम वस्तु का मालिक नहीं होना चाहते हैं:
उदाहरण के लिए:
class A
{
shared_ptr<int> sPtr1;
weak_ptr<int> wPtr1;
}
उपरोक्त वर्ग में wPtr1 wPtr1 द्वारा इंगित संसाधन का मालिक नहीं है। यदि संसाधन हटा दिया गया है तो wPtr1 की समय सीमा समाप्त हो गई है।
परिपत्र निर्भरता से बचने के लिए:
shard_ptr<A> <----| shared_ptr<B> <------
^ | ^ |
| | | |
| | | |
| | | |
| | | |
class A | class B |
| | | |
| ------------ |
| |
-------------------------------------
अब अगर हम वर्ग B और A का साझा_प्रात बनाते हैं, तो दोनों सूचक का उपयोग_काउंट दो है।
जब share_ptr ओड स्कोप से बाहर हो जाता है तो काउंट अभी भी 1 ही रहता है और इसलिए A और B ऑब्जेक्ट डिलीट नहीं होता है।
class B;
class A
{
shared_ptr<B> sP1; // use weak_ptr instead to avoid CD
public:
A() { cout << "A()" << endl; }
~A() { cout << "~A()" << endl; }
void setShared(shared_ptr<B>& p)
{
sP1 = p;
}
};
class B
{
shared_ptr<A> sP1;
public:
B() { cout << "B()" << endl; }
~B() { cout << "~B()" << endl; }
void setShared(shared_ptr<A>& p)
{
sP1 = p;
}
};
int main()
{
shared_ptr<A> aPtr(new A);
shared_ptr<B> bPtr(new B);
aPtr->setShared(bPtr);
bPtr->setShared(aPtr);
return 0;
}
उत्पादन:
A()
B()
जैसा कि हम आउटपुट से देख सकते हैं कि ए और बी पॉइंटर कभी डिलीट नहीं होते हैं और इसलिए मेमोरी लीक होती है।
इस तरह के मुद्दे से बचने के लिए, केवल साझा_प्रतिमा के बजाय कक्षा A में weak_ptr का उपयोग करें जो अधिक समझ में आता है।
मैं देख रहा हूँ std::weak_ptr<T>
एक के रूप में संभाल एक करने के लिए std::shared_ptr<T>
: यह मुझे पाने के लिए अनुमति देता है std::shared_ptr<T>
अगर यह अभी भी मौजूद है, लेकिन यह अपने जीवन का विस्तार नहीं होगा। इस तरह के दृष्टिकोण उपयोगी होने पर कई परिदृश्य हैं:
// Some sort of image; very expensive to create.
std::shared_ptr< Texture > texture;
// A Widget should be able to quickly get a handle to a Texture. On the
// other hand, I don't want to keep Textures around just because a widget
// may need it.
struct Widget {
std::weak_ptr< Texture > texture_handle;
void render() {
if (auto texture = texture_handle.get(); texture) {
// do stuff with texture. Warning: `texture`
// is now extending the lifetime because it
// is a std::shared_ptr< Texture >.
} else {
// gracefully degrade; there's no texture.
}
}
};
एक अन्य महत्वपूर्ण परिदृश्य डेटा संरचनाओं में चक्रों को तोड़ना है।
// Asking for trouble because a node owns the next node, and the next node owns
// the previous node: memory leak; no destructors automatically called.
struct Node {
std::shared_ptr< Node > next;
std::shared_ptr< Node > prev;
};
// Asking for trouble because a parent owns its children and children own their
// parents: memory leak; no destructors automatically called.
struct Node {
std::shared_ptr< Node > parent;
std::shared_ptr< Node > left_child;
std::shared_ptr< Node > right_child;
};
// Better: break dependencies using a std::weak_ptr (but not best way to do it;
// see Herb Sutter's talk).
struct Node {
std::shared_ptr< Node > next;
std::weak_ptr< Node > prev;
};
// Better: break dependencies using a std::weak_ptr (but not best way to do it;
// see Herb Sutter's talk).
struct Node {
std::weak_ptr< Node > parent;
std::shared_ptr< Node > left_child;
std::shared_ptr< Node > right_child;
};
हर्ब सटर में एक उत्कृष्ट बात है जो डिफ़ॉल्ट रूप से लीक फ्रीडम सुनिश्चित करने के लिए भाषा सुविधाओं (इस मामले में स्मार्ट पॉइंटर्स) का सबसे अच्छा उपयोग बताती है (मतलब: निर्माण द्वारा सभी कुछ क्लिक करता है; आप शायद ही इसे खराब कर सकते हैं)। यह एक घड़ी है।