एक "कच्चा" सूचक अप्रबंधित है। वह है, निम्नलिखित पंक्ति:
SomeKindOfObject *someKindOfObject = new SomeKindOfObject();
... यदि एक साथ साथ delete
उचित समय पर क्रियान्वित नहीं किया जाता है तो मेमोरी को लीक कर देगा ।
auto_ptr
इन मामलों को कम करने के लिए, std::auto_ptr<>
पेश किया गया था। 2011 के मानक से पहले C ++ की सीमाओं के कारण, auto_ptr
स्मृति को लीक करना अभी भी बहुत आसान है । यह इस तरह के रूप में सीमित मामलों के लिए पर्याप्त है, हालांकि:
void func() {
std::auto_ptr<SomeKindOfObject> sKOO_ptr(new SomeKindOfObject());
// do some work
// will not leak if you do not copy sKOO_ptr.
}
इसका सबसे कमजोर उपयोग-मामलों में से एक कंटेनर में है। ऐसा इसलिए है क्योंकि यदि किसी की प्रतिलिपि auto_ptr<>
बनाई गई है और पुरानी प्रतिलिपि को सावधानी से रीसेट नहीं किया गया है, तो कंटेनर पॉइंटर को हटा सकता है और डेटा खो सकता है।
unique_ptr
प्रतिस्थापन के रूप में, C ++ 11 पेश किया गया std::unique_ptr<>
:
void func2() {
std::unique_ptr<SomeKindofObject> sKOO_unique(new SomeKindOfObject());
func3(sKOO_unique); // now func3() owns the pointer and sKOO_unique is no longer valid
}
इस तरह के एक unique_ptr<>
को सही ढंग से साफ किया जाएगा, भले ही यह फ़ंक्शन के बीच पारित हो। यह सूचक के "स्वामित्व" का शब्दार्थ रूप से प्रतिनिधित्व करता है - "स्वामी" इसे साफ करता है। यह कंटेनरों में उपयोग के लिए इसे आदर्श बनाता है:
std::vector<std::unique_ptr<SomeKindofObject>> sKOO_vector();
इसके विपरीत auto_ptr<>
, unique_ptr<>
यहाँ अच्छी तरह से व्यवहार किया जाता है, और जब vector
आकार बदलता है, तो कोई भी वस्तु गलती से हटा नहीं दी जाएगी, जबकि vector
इसकी बैकिंग स्टोर की प्रतियां।
shared_ptr
तथा weak_ptr
unique_ptr<>
उपयोगी है, सुनिश्चित करने के लिए, लेकिन ऐसे मामले हैं जहां आप चाहते हैं कि आपके कोड आधार के दो हिस्से समान ऑब्जेक्ट को संदर्भित करने और पॉइंटर को चारों ओर कॉपी करने में सक्षम हों, जबकि अभी भी उचित सफाई की गारंटी दी जा रही है। उदाहरण के लिए, एक पेड़ इस तरह दिख सकता है, जब उपयोग कर रहा हो std::shared_ptr<>
:
template<class T>
struct Node {
T value;
std::shared_ptr<Node<T>> left;
std::shared_ptr<Node<T>> right;
};
इस मामले में, हम एक रूट नोड की कई प्रतियों को भी पकड़ सकते हैं, और रूट नोड की सभी प्रतियां नष्ट होने पर पेड़ को अच्छी तरह से साफ किया जाएगा।
यह काम करता है क्योंकि प्रत्येक shared_ptr<>
वस्तु को न केवल सूचक को रखता है, बल्कि उन सभी shared_ptr<>
वस्तुओं का एक संदर्भ गणना भी है जो एक ही सूचक को संदर्भित करता है। जब कोई नया बनाया जाता है, तो गिनती बढ़ जाती है। जब एक नष्ट हो जाता है, तो गिनती नीचे जाती है। जब गिनती शून्य तक पहुंच जाती है, तो सूचक delete
डी होता है।
तो यह एक समस्या का परिचय देता है: डबल-लिंक्ड संरचनाएं परिपत्र संदर्भों के साथ समाप्त होती हैं। कहें कि हम parent
अपने पेड़ में एक सूचक जोड़ना चाहते हैं Node
:
template<class T>
struct Node {
T value;
std::shared_ptr<Node<T>> parent;
std::shared_ptr<Node<T>> left;
std::shared_ptr<Node<T>> right;
};
अब, यदि हम हटाते हैं, तो Node
इसका चक्रीय संदर्भ है। यह कभी भी delete
d नहीं होगा क्योंकि इसकी संदर्भ संख्या कभी भी शून्य नहीं होगी।
इस समस्या को हल करने के लिए, आप एक का उपयोग करें std::weak_ptr<>
:
template<class T>
struct Node {
T value;
std::weak_ptr<Node<T>> parent;
std::shared_ptr<Node<T>> left;
std::shared_ptr<Node<T>> right;
};
अब, चीजें सही तरीके से काम करेंगी, और एक नोड को हटाने से मूल नोड के लिए अटक संदर्भ नहीं छोड़ेगा। यह पेड़ को थोड़ा और जटिल बनाता है, हालांकि:
std::shared_ptr<Node<T>> parent_of_this = node->parent.lock();
इस तरह, आप नोड के संदर्भ को लॉक कर सकते हैं, और जब आप इस पर काम कर रहे होते हैं, तो आपके पास एक उचित गारंटी है कि यह गायब नहीं होगा shared_ptr<>
।
make_shared
तथा make_unique
अब, कुछ छोटी-मोटी समस्याएं हैं shared_ptr<>
और unique_ptr<>
जिनका समाधान किया जाना चाहिए। निम्नलिखित दो पंक्तियों में एक समस्या है:
foo_unique(std::unique_ptr<SomeKindofObject>(new SomeKindOfObject()), thrower());
foo_shared(std::shared_ptr<SomeKindofObject>(new SomeKindOfObject()), thrower());
यदि thrower()
एक अपवाद फेंकता है, तो दोनों लाइनें मेमोरी को लीक कर देंगी। और उस से भी अधिक, shared_ptr<>
संदर्भ गिनती रखती दूर वस्तु से दूर यह की ओर इशारा करता है और इस कर सकते हैं एक दूसरे आवंटन मतलब)। यह आमतौर पर वांछनीय नहीं है।
C ++ 11 प्रदान करता है std::make_shared<>()
और C ++ 14 std::make_unique<>()
इस समस्या को हल करने के लिए प्रदान करता है:
foo_unique(std::make_unique<SomeKindofObject>(), thrower());
foo_shared(std::make_shared<SomeKindofObject>(), thrower());
अब, दोनों मामलों में, भले ही thrower()
एक अपवाद फेंकता हो, स्मृति का रिसाव नहीं होगा। एक बोनस के रूप में, अपने प्रबंधित ऑब्जेक्ट के रूप में एक ही मेमोरी स्पेस मेंmake_shared<>()
अपनी संदर्भ गणना बनाने का अवसर है , जो दोनों तेजी से हो सकता है और आपको एक अपवाद सुरक्षा गारंटी देते हुए मेमोरी के कुछ बाइट्स बचा सकता है!
Qt के बारे में नोट्स
हालांकि, यह ध्यान दिया जाना चाहिए, कि क्यूटी, जो पूर्व-सी ++ 11 संकलक का समर्थन करना चाहिए, का अपना कचरा-संग्रह मॉडल है: कई QObject
एस में एक तंत्र है जहां वे उपयोगकर्ता की आवश्यकता के बिना उन्हें ठीक से नष्ट कर delete
देंगे।
मुझे नहीं पता कि QObject
C ++ 11 प्रबंधित पॉइंटर्स द्वारा प्रबंधित किए जाने पर s कैसे व्यवहार करेगा, इसलिए मैं यह नहीं कह सकता कि shared_ptr<QDialog>
यह एक अच्छा विचार है। मुझे निश्चित रूप से कहने के लिए Qt के साथ पर्याप्त अनुभव नहीं है, लेकिन मेरा मानना है कि Qt5 को इस उपयोग के मामले के लिए समायोजित किया गया है।