C++
स्मार्ट पॉइंटर्स
खोज…
वाक्य - विन्यास
-
std::shared_ptr<ClassType> variableName = std::make_shared<ClassType>(arg1, arg2, ...);
-
std::shared_ptr<ClassType> variableName (new ClassType(arg1, arg2, ...));
-
std::unique_ptr<ClassType> variableName = std::make_unique<ClassType>(arg1, arg2, ...);
// सी ++ 14 -
std::unique_ptr<ClassType> variableName (new ClassType(arg1, arg2, ...));
टिप्पणियों
C ++ एक मेमोरी-प्रबंधित भाषा नहीं है। डायनामिक रूप से आवंटित मेमोरी (यानी new
के साथ बनाई गई वस्तुएं) "लीक" हो जाएंगी यदि यह स्पष्ट रूप से डीलिट ( delete
साथ) नहीं है। यह सुनिश्चित करने के लिए प्रोग्रामर की जिम्मेदारी है कि गतिशील रूप से आवंटित मेमोरी को उस ऑब्जेक्ट के अंतिम पॉइंटर को छोड़ने से पहले मुक्त कर दिया जाए।
स्मार्ट पॉइंटर्स का उपयोग डायनेमिक रूप से आवंटित मेमोरी के दायरे को स्वचालित रूप से प्रबंधित करने के लिए किया जा सकता है (यानी जब अंतिम पॉइंटर संदर्भ उस दायरे से बाहर चला जाता है)।
ज्यादातर मामलों में स्मार्ट पॉइंटर्स को "कच्चे" पॉइंटर्स से अधिक पसंद किया जाता है। वे गतिशील रूप से आवंटित स्मृति के स्वामित्व अर्थ को स्पष्ट करते हैं, उनके नामों में संवाद करके कि क्या कोई वस्तु साझा करने का इरादा है या विशिष्ट स्वामित्व है।
स्मार्ट पॉइंटर्स का उपयोग करने में सक्षम होने के लिए #include <memory>
का उपयोग करें।
स्वामित्व साझा करना (std :: साझा_प्ट्र)
वर्ग टेम्पलेट std::shared_ptr
एक साझा पॉइंटर को परिभाषित करता है जो अन्य साझा किए गए बिंदुओं के साथ किसी वस्तु के स्वामित्व को साझा करने में सक्षम है। यह std::unique_ptr
विपरीत है जो अनन्य स्वामित्व का प्रतिनिधित्व करता है।
साझा व्यवहार को संदर्भ गिनती के रूप में ज्ञात तकनीक के माध्यम से कार्यान्वित किया जाता है, जहां ऑब्जेक्ट को इंगित करने वाले साझा संकेत की संख्या इसके साथ संग्रहीत होती है। जब यह संख्या शून्य तक पहुँचती है, तो अंतिम std::shared_ptr
उदाहरण के विनाश या पुनर्मूल्यांकन के माध्यम से, वस्तु स्वतः नष्ट हो जाती है।
// Creation: 'firstShared' is a shared pointer for a new instance of 'Foo'
std::shared_ptr<Foo> firstShared = std::make_shared<Foo>(/*args*/);
एक ही ऑब्जेक्ट को साझा करने वाले कई स्मार्ट पॉइंटर्स बनाने के लिए, हमें एक और shared_ptr
बनाने की आवश्यकता है जो कि पहले साझा किए गए पॉइंटर को एलियास करता है। यहाँ यह करने के 2 तरीके हैं:
std::shared_ptr<Foo> secondShared(firstShared); // 1st way: Copy constructing
std::shared_ptr<Foo> secondShared;
secondShared = firstShared; // 2nd way: Assigning
या तो ऊपर तरीकों में से बनाता है secondShared
एक साझा सूचक है कि हमारे उदाहरण के शेयरों स्वामित्व Foo
के साथ firstShared
।
स्मार्ट पॉइंटर कच्चे पॉइंटर की तरह ही काम करता है। इस का मतलब है, तो आप उपयोग कर सकते हैं *
उन्हें भिन्नता है। नियमित ->
ऑपरेटर भी काम करता है:
secondShared->test(); // Calls Foo::test()
अंत में, जब अंतिम अलियास shared_ptr
कार्यक्षेत्र से बाहर हो जाता है, तो हमारे Foo
उदाहरण के विध्वंसक को कहा जाता है।
चेतावनी: जब साझा स्वामित्व शब्दार्थ के लिए अतिरिक्त डेटा आवंटित किए जाने की आवश्यकता हो, तो एक shared_ptr
निर्माण एक bad_alloc
अपवाद को फेंक सकता है। यदि कंस्ट्रक्टर को एक नियमित पॉइंटर पास किया जाता है, तो वह इंगित की गई वस्तु के स्वामी होने का अनुमान लगाता है और यदि अपवाद को फेंक दिया जाता है, तो उसे हटाने वाला कहता है। इसका मतलब है कि shared_ptr<T>(new T(args))
एक T
ऑब्जेक्ट को लीक नहीं करेगा यदि shared_ptr<T>
का आवंटन विफल हो जाता है। हालांकि, make_shared<T>(args)
या make_shared<T>(args)
allocate_shared<T>(alloc, args)
make_shared<T>(args)
का उपयोग करने की सलाह दी जाती है, जो कार्यान्वयन को मेमोरी आवंटन का अनुकूलन करने में सक्षम बनाता है।
शेयर्स_प्ट्र का उपयोग करते हुए एरेस ([]) आवंटित करें
दुर्भाग्य से, make_shared<>
का उपयोग करके एरे को आवंटित करने का कोई सीधा तरीका नहीं है।
shared_ptr<>
लिए new
और std::default_delete
का उपयोग करके shared_ptr<>
बनाना संभव है।
उदाहरण के लिए, 10 पूर्णांकों की एक सरणी आवंटित करने के लिए, हम कोड को इस प्रकार लिख सकते हैं
shared_ptr<int> sh(new int[10], std::default_delete<int[]>());
निर्दिष्ट करने वाली std::default_delete
यह सुनिश्चित करने के लिए यहां अनिवार्य है कि delete[]
गए मेमोरी को delete[]
का उपयोग करके सही ढंग से साफ किया गया है।
यदि हम संकलन समय पर आकार जानते हैं, तो हम इसे इस तरह से कर सकते हैं:
template<class Arr>
struct shared_array_maker {};
template<class T, std::size_t N>
struct shared_array_maker<T[N]> {
std::shared_ptr<T> operator()const{
auto r = std::make_shared<std::array<T,N>>();
if (!r) return {};
return {r.data(), r};
}
};
template<class Arr>
auto make_shared_array()
-> decltype( shared_array_maker<Arr>{}() )
{ return shared_array_maker<Arr>{}(); }
तो make_shared_array<int[10]>
रिटर्न एक shared_ptr<int>
10 ints सभी डिफ़ॉल्ट निर्माण की ओर इशारा करते।
C ++ 17 के साथ, shared_ptr
ने सरणी प्रकारों के लिए विशेष समर्थन प्राप्त किया । यह स्पष्ट रूप से सरणी-डेलेटर को निर्दिष्ट करने के लिए आवश्यक नहीं है, और साझा सूचक को []
सरणी इंडेक्स ऑपरेटर का उपयोग करके dereferenced जा सकता है:
std::shared_ptr<int[]> sh(new int[10]);
sh[0] = 42;
साझा संकेतकर्ता उस वस्तु के उप-ऑब्जेक्ट को इंगित कर सकता है जो उसका मालिक है:
struct Foo { int x; };
std::shared_ptr<Foo> p1 = std::make_shared<Foo>();
std::shared_ptr<int> p2(p1, &p1->x);
p2
और p1
दोनों ही Foo
प्रकार की वस्तु के मालिक हैं, लेकिन p2
अपने int
सदस्य x
को इंगित करता है। इसका मतलब यह है कि यदि p1
कार्यक्षेत्र से बाहर चला जाता है या फिर से असाइन किया जाता है, तो अंतर्निहित Foo
ऑब्जेक्ट अभी भी जीवित रहेगा, यह सुनिश्चित करता है कि p2
नहीं है।
महत्वपूर्ण: एक shared_ptr
केवल अपने बारे में और अन्य सभी shared_ptr
बारे में जानता है जो कि उर्फ कंस्ट्रक्टर के साथ बनाए गए थे। यह किसी भी अन्य बिंदुओं के बारे में नहीं जानता है, जिसमें एक ही Foo
उदाहरण के संदर्भ में बनाए गए अन्य सभी shared_ptr
शामिल हैं:
Foo *foo = new Foo;
std::shared_ptr<Foo> shared1(foo);
std::shared_ptr<Foo> shared2(foo); // don't do this
shared1.reset(); // this will delete foo, since shared1
// was the only shared_ptr that owned it
shared2->test(); // UNDEFINED BEHAVIOR: shared2's foo has been
// deleted already!!
Share_ptr का स्वामित्व स्थानांतरण
डिफ़ॉल्ट रूप से, shared_ptr
संदर्भ गणना बढ़ाता है और स्वामित्व स्थानांतरित नहीं करता है। हालाँकि, यह std::move
का उपयोग करके स्वामित्व को स्थानांतरित करने के लिए बनाया जा सकता है:
shared_ptr<int> up = make_shared<int>();
// Transferring the ownership
shared_ptr<int> up2 = move(up);
// At this point, the reference count of up = 0 and the
// ownership of the pointer is solely with up2 with reference count = 1
अस्थायी स्वामित्व के साथ साझा करना (std :: weak_ptr)
के उदाहरण std::weak_ptr
के उदाहरण के स्वामित्व वाले वस्तुओं को इंगित कर सकते std::shared_ptr
जबकि केवल अस्थायी मालिकों बनने के लिए खुद को। इसका मतलब यह है कि कमजोर संकेत वस्तु के संदर्भ की संख्या में परिवर्तन नहीं करते हैं और इसलिए किसी वस्तु के विलोपन को नहीं रोकते हैं यदि वस्तु के सभी साझा बिंदु पुन: असाइन या नष्ट हो जाते हैं।
निम्न उदाहरण में std::weak_ptr
का उपयोग किया जाता है ताकि किसी पेड़ की वस्तु का विनाश न हो।
#include <memory>
#include <vector>
struct TreeNode {
std::weak_ptr<TreeNode> parent;
std::vector< std::shared_ptr<TreeNode> > children;
};
int main() {
// Create a TreeNode to serve as the root/parent.
std::shared_ptr<TreeNode> root(new TreeNode);
// Give the parent 100 child nodes.
for (size_t i = 0; i < 100; ++i) {
std::shared_ptr<TreeNode> child(new TreeNode);
root->children.push_back(child);
child->parent = root;
}
// Reset the root shared pointer, destroying the root object, and
// subsequently its child nodes.
root.reset();
}
जैसा कि चाइल्ड नोड्स को नोड नोड के बच्चों के साथ जोड़ा जाता है, उनके std::weak_ptr
सदस्य parent
को नोड नोड में सेट किया जाता है। सदस्य parent
को एक साझा सूचक के विपरीत एक कमजोर सूचक के रूप में घोषित किया जाता है, जैसे कि रूट नोड का संदर्भ गणना बढ़ाना नहीं है। जब रूट नोड main()
के अंत में रीसेट हो जाता है, तो रूट नष्ट हो जाता है। चूंकि केवल शेष std::shared_ptr
किए गए चाइल्ड नोड्स के संदर्भों को रूट के संग्रह children
में समाहित किया गया था, सभी बच्चे नोड्स को बाद में नष्ट कर दिया जाता है।
नियंत्रण ब्लॉक कार्यान्वयन विवरण के कारण, shared_ptr आबंटित स्मृति तक जारी नहीं किया जा सकता है shared_ptr
संदर्भ काउंटर और weak_ptr
संदर्भ काउंटर दोनों पहुंच शून्य।
#include <memory>
int main()
{
{
std::weak_ptr<int> wk;
{
// std::make_shared is optimized by allocating only once
// while std::shared_ptr<int>(new int(42)) allocates twice.
// Drawback of std::make_shared is that control block is tied to our integer
std::shared_ptr<int> sh = std::make_shared<int>(42);
wk = sh;
// sh memory should be released at this point...
}
// ... but wk is still alive and needs access to control block
}
// now memory is released (sh and wk)
}
चूँकि std::weak_ptr
अपनी संदर्भित वस्तु को जीवित नहीं रखता है, एक std::weak_ptr
माध्यम से प्रत्यक्ष डेटा का उपयोग std::weak_ptr
संभव नहीं है। इसके बजाय यह एक lock()
सदस्य फ़ंक्शन प्रदान करता है जो संदर्भित ऑब्जेक्ट के लिए एक std::shared_ptr
प्राप्त करने का प्रयास करता है:
#include <cassert>
#include <memory>
int main()
{
{
std::weak_ptr<int> wk;
std::shared_ptr<int> sp;
{
std::shared_ptr<int> sh = std::make_shared<int>(42);
wk = sh;
// calling lock will create a shared_ptr to the object referenced by wk
sp = wk.lock();
// sh will be destroyed after this point, but sp is still alive
}
// sp still keeps the data alive.
// At this point we could even call lock() again
// to retrieve another shared_ptr to the same data from wk
assert(*sp == 42);
assert(!wk.expired());
// resetting sp will delete the data,
// as it is currently the last shared_ptr with ownership
sp.reset();
// attempting to lock wk now will return an empty shared_ptr,
// as the data has already been deleted
sp = wk.lock();
assert(!sp);
assert(wk.expired());
}
}
अद्वितीय स्वामित्व (std :: unique_ptr)
एक std::unique_ptr
एक वर्ग टेम्पलेट है जो गतिशील रूप से संग्रहीत ऑब्जेक्ट के जीवनकाल का प्रबंधन करता है। std::shared_ptr
विपरीत, डायनामिक ऑब्जेक्ट का स्वामित्व केवल std::unique_ptr
केवल एक उदाहरण से होता है std::unique_ptr
किसी भी समय,
// Creates a dynamic int with value of 20 owned by a unique pointer
std::unique_ptr<int> ptr = std::make_unique<int>(20);
(नोट: std::unique_ptr
C ++ 11 और std::make_unique
C ++ 14 के बाद से उपलब्ध है।)
केवल चर ptr
एक गतिशील रूप से आवंटित int
लिए एक सूचक रखता है। जब एक अद्वितीय पॉइंटर जो किसी वस्तु के मालिक के दायरे से बाहर चला जाता है, तो स्वामित्व वाली वस्तु को हटा दिया जाता है, अर्थात इसके डिस्ट्रक्टर को कहा जाता है यदि ऑब्जेक्ट क्लास प्रकार का है, और उस ऑब्जेक्ट के लिए मेमोरी जारी की जाती है।
std::unique_ptr
और std::make_unique
उपयोग सरणी प्रकारों के साथ करने के लिए, उनकी सरणी विशेषज्ञता का उपयोग करें:
// Creates a unique_ptr to an int with value 59
std::unique_ptr<int> ptr = std::make_unique<int>(59);
// Creates a unique_ptr to an array of 15 ints
std::unique_ptr<int[]> ptr = std::make_unique<int[]>(15);
आप कच्चे सूचक की तरह std::unique_ptr
उपयोग कर सकते हैं, क्योंकि यह उन ऑपरेटरों को ओवरलोड करता है।
आप एक स्मार्ट पॉइंटर की सामग्री का स्वामित्व std::move
उपयोग से दूसरे पॉइंटर में std::move
, जिससे मूल स्मार्ट पॉइंटर nullptr
को इंगित nullptr
।
// 1. std::unique_ptr
std::unique_ptr<int> ptr = std::make_unique<int>();
// Change value to 1
*ptr = 1;
// 2. std::unique_ptr (by moving 'ptr' to 'ptr2', 'ptr' doesn't own the object anymore)
std::unique_ptr<int> ptr2 = std::move(ptr);
int a = *ptr2; // 'a' is 1
int b = *ptr; // undefined behavior! 'ptr' is 'nullptr'
// (because of the move command above)
पैरामीटर के रूप में फ़ंक्शन के लिए unique_ptr
करना:
void foo(std::unique_ptr<int> ptr)
{
// Your code goes here
}
std::unique_ptr<int> ptr = std::make_unique<int>(59);
foo(std::move(ptr))
फ़ंक्शन से unique_ptr
को वापस करना। यह फ़ैक्टरी फ़ंक्शंस लिखने का पसंदीदा C ++ 11 तरीका है, क्योंकि यह स्पष्ट रूप से रिटर्न के स्वामित्व शब्दार्थ को बताता है: कॉल करने वाला परिणामी unique_ptr
का मालिक है और इसके लिए जिम्मेदार है।
std::unique_ptr<int> foo()
{
std::unique_ptr<int> ptr = std::make_unique<int>(59);
return ptr;
}
std::unique_ptr<int> ptr = foo();
इसकी तुलना करें:
int* foo_cpp03();
int* p = foo_cpp03(); // do I own p? do I have to delete it at some point?
// it's not readily apparent what the answer is.
वर्ग टेम्पलेट make_unique
C ++ 14 के बाद से प्रदान किया गया है। इसे C ++ 11 कोड में मैन्युअल रूप से जोड़ना आसान है:
template<typename T, typename... Args>
typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
make_unique(Args&&... args)
{ return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); }
// Use make_unique for arrays
template<typename T>
typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
make_unique(size_t n)
{ return std::unique_ptr<T>(new typename std::remove_extent<T>::type[n]()); }
गूंगा स्मार्ट सूचक (विपरीत std::auto_ptr
), unique_ptr
भी वेक्टर आवंटन के साथ instantiated जा सकता है (नहीं std::vector
)। पहले के उदाहरण अदिश आवंटन के लिए थे। उदाहरण के लिए 10 तत्वों के लिए एक गतिशील रूप से आवंटित पूर्णांक सरणी, आप टेम्पलेट प्रकार के रूप में int[]
निर्दिष्ट करेंगे (और न केवल int
):
std::unique_ptr<int[]> arr_ptr = std::make_unique<int[]>(10);
जिसे सरल बनाया जा सकता है:
auto arr_ptr = std::make_unique<int[]>(10);
अब, आप arr_ptr
उपयोग arr_ptr
जैसे कि यह एक सरणी है:
arr_ptr[2] = 10; // Modify third element
आपको डी-आवंटन के बारे में चिंता करने की आवश्यकता नहीं है। यह टेम्प्लेट विशेष संस्करण रचनाकारों और विध्वंसक को उचित रूप से कॉल करता है। unique_ptr
या खुद एक vector
के unique_ptr
संस्करण का उपयोग करना - एक व्यक्तिगत पसंद है।
C ++ 11 से पहले के संस्करणों में, std::auto_ptr
उपलब्ध था। unique_ptr
विपरीत इसे auto_ptr
s की प्रतिलिपि बनाने की अनुमति है, जिस पर स्रोत ptr
निहित सूचक के स्वामित्व को खो देगा और लक्ष्य इसे प्राप्त करता है।
C इंटरफ़ेस के लिए एक आवरण बनाने के लिए कस्टम हटाने वालों का उपयोग करना
कई सी इंटरफेस जैसे SDL2 के अपने विलोपन कार्य हैं। इसका मतलब है कि आप सीधे स्मार्ट पॉइंटर्स का उपयोग नहीं कर सकते हैं:
std::unique_ptr<SDL_Surface> a; // won't work, UNSAFE!
इसके बजाय, आपको अपने स्वयं के डिलेटर को परिभाषित करने की आवश्यकता है। यहाँ उदाहरण SDL_Surface
संरचना का उपयोग करते हैं जिसे SDL_FreeSurface()
फ़ंक्शन का उपयोग करके मुक्त किया जाना चाहिए, लेकिन उन्हें कई अन्य सी इंटरफेस के अनुकूल होना चाहिए।
डिलेटर को पॉइंटर पॉइंटर के साथ कॉल करने योग्य होना चाहिए, और इसलिए यह एक साधारण फ़ंक्शन पॉइंटर हो सकता है:
std::unique_ptr<SDL_Surface, void(*)(SDL_Surface*)> a(pointer, SDL_FreeSurface);
कोई अन्य कॉल करने योग्य वस्तु भी काम करेगी, उदाहरण के लिए एक operator()
साथ एक वर्ग operator()
:
struct SurfaceDeleter {
void operator()(SDL_Surface* surf) {
SDL_FreeSurface(surf);
}
};
std::unique_ptr<SDL_Surface, SurfaceDeleter> a(pointer, SurfaceDeleter{}); // safe
std::unique_ptr<SDL_Surface, SurfaceDeleter> b(pointer); // equivalent to the above
// as the deleter is value-initialized
यह न केवल आपको सुरक्षित, शून्य ओवरहेड (यदि आप unique_ptr
उपयोग unique_ptr
) स्वचालित मेमोरी प्रबंधन प्रदान करता है, तो आपको अपवाद सुरक्षा भी मिलती है।
ध्यान दें कि unique_ptr
के लिए प्रकार का हिस्सा है, और कार्यान्वयन खाली आधार अनुकूलन का उपयोग कर सकता है ताकि खाली कस्टम हटाने वालों के लिए आकार में कोई परिवर्तन न हो। तो जबकि std::unique_ptr<SDL_Surface, SurfaceDeleter>
और std::unique_ptr<SDL_Surface, void(*)(SDL_Surface*)>
एक ही तरह से एक ही समस्या को हल करते हैं, पूर्व प्रकार अभी भी केवल एक पॉइंटर के आकार का है, जबकि बाद के प्रकार को दो पॉइंटर्स रखने हैं: दोनों SDL_Surface*
और फंक्शन पॉइंटर! नि: शुल्क फ़ंक्शन कस्टम हटाने वाले होने पर, फ़ंक्शन को खाली प्रकार में लपेटना बेहतर होता है।
ऐसे मामलों में जहां संदर्भ गिनती महत्वपूर्ण है, एक एक इस्तेमाल कर सकते हैं shared_ptr
एक के बजाय unique_ptr
। shared_ptr
हमेशा एक डिलेटर को स्टोर करता है, इससे डीलेटर का प्रकार मिट जाता है, जो कि एपीआई में उपयोगी हो सकता है। unique_ptr
पर unique_ptr
का shared_ptr
करने के unique_ptr
में unique_ptr
को संचय करने के लिए उच्चतर स्मृति लागत और संदर्भ संख्या को बनाए रखने के लिए एक प्रदर्शन लागत शामिल है।
// deleter required at construction time and is part of the type
std::unique_ptr<SDL_Surface, void(*)(SDL_Surface*)> a(pointer, SDL_FreeSurface);
// deleter is only required at construction time, not part of the type
std::shared_ptr<SDL_Surface> b(pointer, SDL_FreeSurface);
template auto
साथ, हम अपने कस्टम डिलीटर्स को लपेटना और भी आसान बना सकते हैं:
template <auto DeleteFn>
struct FunctionDeleter {
template <class T>
void operator()(T* ptr) {
DeleteFn(ptr);
}
};
template <class T, auto DeleteFn>
using unique_ptr_deleter = std::unique_ptr<T, FunctionDeleter<DeleteFn>>;
जिसके साथ उपरोक्त उदाहरण बस है:
unique_ptr_deleter<SDL_Surface, SDL_FreeSurface> c(pointer);
यहाँ, के प्रयोजन के auto
सभी नि: शुल्क कार्यों को संभालने के लिए, चाहे वे लौट है void
(जैसे SDL_FreeSurface
) या नहीं (जैसे fclose
)।
बिना शब्दार्थ के अद्वितीय स्वामित्व (auto_ptr)
नोट :: std::auto_ptr
को C ++ 11 में पदावनत किया गया है और C ++ 17 में निकाला जाएगा। आपको इसका उपयोग केवल तभी करना चाहिए जब आप C ++ 03 या उससे पहले का उपयोग करने के लिए मजबूर हों और सावधानी बरतने के लिए तैयार हों। यह std::move
साथ संयोजन में unique_ptr पर जाने की सिफारिश की गई है std::move
को बदलने के लिए std::move
std::auto_ptr
व्यवहार।
इससे पहले कि हम std::unique_ptr
, इससे पहले कि हम शब्दार्थ को आगे std::unique_ptr
, हमने std::auto_ptr
। std::auto_ptr
अद्वितीय स्वामित्व प्रदान करता है लेकिन प्रतिलिपि पर स्वामित्व स्थानांतरित करता है।
सभी स्मार्ट पॉइंटर्स के साथ, std::auto_ptr
स्वचालित रूप से संसाधनों को साफ करता है ( RAII देखें):
{
std::auto_ptr<int> p(new int(42));
std::cout << *p;
} // p is deleted here, no memory leaked
लेकिन केवल एक ही मालिक को अनुमति देता है:
std::auto_ptr<X> px = ...;
std::auto_ptr<X> py = px;
// px is now empty
यह बिना स्वामित्व खोने के खतरे पर स्वामित्व स्पष्ट और अद्वितीय रखने के लिए std :: auto_ptr का उपयोग करने की अनुमति देता है:
void f(std::auto_ptr<X> ) {
// assumes ownership of X
// deletes it at end of scope
};
std::auto_ptr<X> px = ...;
f(px); // f acquires ownership of underlying X
// px is now empty
px->foo(); // NPE!
// px.~auto_ptr() does NOT delete
स्वामित्व का हस्तांतरण "कॉपी" कंस्ट्रक्टर में हुआ। auto_ptr
के कॉपी कंस्ट्रक्टर और कॉपी असाइनमेंट ऑपरेटर अपने संचालकों को नॉन- const
संदर्भ द्वारा लेते हैं ताकि उन्हें संशोधित किया जा सके। एक उदाहरण कार्यान्वयन हो सकता है:
template <typename T>
class auto_ptr {
T* ptr;
public:
auto_ptr(auto_ptr& rhs)
: ptr(rhs.release())
{ }
auto_ptr& operator=(auto_ptr& rhs) {
reset(rhs.release());
return *this;
}
T* release() {
T* tmp = ptr;
ptr = nullptr;
return tmp;
}
void reset(T* tmp = nullptr) {
if (ptr != tmp) {
delete ptr;
ptr = tmp;
}
}
/* other functions ... */
};
यह कॉपी शब्दार्थों को तोड़ता है, जिसके लिए आवश्यक है कि किसी ऑब्जेक्ट को कॉपी करने से आप उसके दो बराबर संस्करण छोड़ दें। किसी भी प्रकार की प्रतिलिपि के लिए, T
, मुझे लिखने में सक्षम होना चाहिए:
T a = ...;
T b(a);
assert(b == a);
लेकिन auto_ptr
, यह मामला नहीं है। परिणामस्वरूप, कंटेनरों में auto_ptr
s डालना सुरक्षित नहीं है।
इस बात का जिक्र करते हुए साझा किया जा रहा है
enable_shared_from_this
एक वैध प्राप्त कर सकती हैं shared_ptr
के उदाहरण this
।
वर्ग टेम्पलेट से अपनी कक्षा पाने तक enable_shared_from_this
, आप एक विधि के वारिस shared_from_this
कि एक रिटर्न shared_ptr
के उदाहरण this
।
ध्यान दें कि ऑब्जेक्ट को पहले स्थान पर एक shared_ptr
रूप में बनाया जाना चाहिए:
#include <memory>
class A: public enable_shared_from_this<A> {
};
A* ap1 =new A();
shared_ptr<A> ap2(ap1); // First prepare a shared pointer to the object and hold it!
// Then get a shared pointer to the object from the object itself
shared_ptr<A> ap3 = ap1->shared_from_this();
int c3 =ap3.use_count(); // =2: pointing to the same object
नोट (2) आप enable_shared_from_this
अंदर enable_shared_from_this
को कॉल नहीं कर सकते हैं।
#include <memory> // enable_shared_from_this
class Widget : public std::enable_shared_from_this< Widget >
{
public:
void DoSomething()
{
std::shared_ptr< Widget > self = shared_from_this();
someEvent -> Register( self );
}
private:
...
};
int main()
{
...
auto w = std::make_shared< Widget >();
w -> DoSomething();
...
}
यदि आप shared_ptr
किए गए ऑब्जेक्ट का shared_ptr
, जैसे कि स्थानीय स्वचालित ऑब्जेक्ट या वैश्विक ऑब्जेक्ट के रूप में shared_ptr
किए गए एक shared_from_this()
पर नहीं, तो व्यवहार अपरिभाषित है। C ++ 17 के बाद से इसके बजाय std::bad_alloc
फेंकता है।
का उपयोग करते हुए shared_from_this()
एक निर्माता से एक के स्वामित्व में नहीं एक वस्तु पर इसे का उपयोग करने के बराबर है shared_ptr
क्योंकि वस्तुओं के पास है, shared_ptr
निर्माता रिटर्न के बाद।
कास्टिंग std :: share_ptr पॉइंटर्स
यह सीधे उपयोग करने के लिए संभव नहीं है static_cast
, const_cast
, dynamic_cast
और reinterpret_cast
पर std::shared_ptr
सूचक तर्क के रूप में पारित किया जा रहा के साथ एक सूचक साझा करने स्वामित्व पुनः प्राप्त करने के। इसके बजाय, कार्यों std::static_pointer_cast
, std::const_pointer_cast
, std::dynamic_pointer_cast
और std::reinterpret_pointer_cast
इस्तेमाल किया जाना चाहिए:
struct Base { virtual ~Base() noexcept {}; };
struct Derived: Base {};
auto derivedPtr(std::make_shared<Derived>());
auto basePtr(std::static_pointer_cast<Base>(derivedPtr));
auto constBasePtr(std::const_pointer_cast<Base const>(basePtr));
auto constDerivedPtr(std::dynamic_pointer_cast<Derived const>(constBasePtr));
ध्यान दें कि std::reinterpret_pointer_cast
C ++ 11 और C ++ 14 में उपलब्ध नहीं है, क्योंकि यह केवल N3920 द्वारा प्रस्तावित किया गया था और फरवरी 2014 में लाइब्रेरी फंडामेंटल टीएस में अपनाया गया था। हालाँकि, इसे निम्नानुसार लागू किया जा सकता है:
template <typename To, typename From>
inline std::shared_ptr<To> reinterpret_pointer_cast(
std::shared_ptr<From> const & ptr) noexcept
{ return std::shared_ptr<To>(ptr, reinterpret_cast<To *>(ptr.get())); }
स्मार्ट पॉइंटर लिखना: value_ptr
एक value_ptr
एक स्मार्ट पॉइंटर है जो एक मान की तरह व्यवहार करता है। जब कॉपी की जाती है, तो यह उसकी सामग्री की प्रतिलिपि बनाता है। निर्मित होने पर, यह अपनी सामग्री बनाता है।
// Like std::default_delete:
template<class T>
struct default_copier {
// a copier must handle a null T const* in and return null:
T* operator()(T const* tin)const {
if (!tin) return nullptr;
return new T(*tin);
}
void operator()(void* dest, T const* tin)const {
if (!tin) return;
return new(dest) T(*tin);
}
};
// tag class to handle empty case:
struct empty_ptr_t {};
constexpr empty_ptr_t empty_ptr{};
// the value pointer type itself:
template<class T, class Copier=default_copier<T>, class Deleter=std::default_delete<T>,
class Base=std::unique_ptr<T, Deleter>
>
struct value_ptr:Base, private Copier {
using copier_type=Copier;
// also typedefs from unique_ptr
using Base::Base;
value_ptr( T const& t ):
Base( std::make_unique<T>(t) ),
Copier()
{}
value_ptr( T && t ):
Base( std::make_unique<T>(std::move(t)) ),
Copier()
{}
// almost-never-empty:
value_ptr():
Base( std::make_unique<T>() ),
Copier()
{}
value_ptr( empty_ptr_t ) {}
value_ptr( Base b, Copier c={} ):
Base(std::move(b)),
Copier(std::move(c))
{}
Copier const& get_copier() const {
return *this;
}
value_ptr clone() const {
return {
Base(
get_copier()(this->get()),
this->get_deleter()
),
get_copier()
};
}
value_ptr(value_ptr&&)=default;
value_ptr& operator=(value_ptr&&)=default;
value_ptr(value_ptr const& o):value_ptr(o.clone()) {}
value_ptr& operator=(value_ptr const&o) {
if (o && *this) {
// if we are both non-null, assign contents:
**this = *o;
} else {
// otherwise, assign a clone (which could itself be null):
*this = o.clone();
}
return *this;
}
value_ptr& operator=( T const& t ) {
if (*this) {
**this = t;
} else {
*this = value_ptr(t);
}
return *this;
}
value_ptr& operator=( T && t ) {
if (*this) {
**this = std::move(t);
} else {
*this = value_ptr(std::move(t));
}
return *this;
}
T& get() { return **this; }
T const& get() const { return **this; }
T* get_pointer() {
if (!*this) return nullptr;
return std::addressof(get());
}
T const* get_pointer() const {
if (!*this) return nullptr;
return std::addressof(get());
}
// operator-> from unique_ptr
};
template<class T, class...Args>
value_ptr<T> make_value_ptr( Args&&... args ) {
return {std::make_unique<T>(std::forward<Args>(args)...)};
}
यह विशेष value_ptr केवल तभी खाली होता है जब आप इसे empty_ptr_t
साथ empty_ptr_t
या यदि आप इससे आगे बढ़ते हैं। यह इस तथ्य को उजागर करता है कि यह एक unique_ptr
, इसलिए इस पर explicit operator bool() const
unique_ptr
काम करता है। .get()
एक संदर्भ को वापस करने के लिए बदल दिया गया है (जैसा कि यह लगभग कभी खाली नहीं होता है), और .get_pointer()
इसके बजाय एक सूचक लौटाता है।
यह स्मार्ट पॉइंटर pImpl
मामलों के लिए उपयोगी हो सकता है, जहां हम मूल्य-शब्दार्थ चाहते हैं, लेकिन हम कार्यान्वयन फ़ाइल के बाहर pImpl
की सामग्री को उजागर नहीं करना चाहते हैं।
एक गैर-डिफ़ॉल्ट Copier
, यह आभासी आधार वर्गों को भी संभाल सकता है जो जानता है कि उनके व्युत्पन्न के उदाहरणों का उत्पादन कैसे करें और उन्हें मूल्य-प्रकार में बदल दें।