सवाल वास्तव में शीर्षक में फिट बैठता है: मैं यह जानने के लिए उत्सुक हूं कि इस अंतर का तकनीकी कारण क्या है, लेकिन यह भी तर्क है?
std::shared_ptr<void> sharedToVoid; // legal;
std::unique_ptr<void> uniqueToVoid; // ill-formed;
सवाल वास्तव में शीर्षक में फिट बैठता है: मैं यह जानने के लिए उत्सुक हूं कि इस अंतर का तकनीकी कारण क्या है, लेकिन यह भी तर्क है?
std::shared_ptr<void> sharedToVoid; // legal;
std::unique_ptr<void> uniqueToVoid; // ill-formed;
जवाबों:
ऐसा इसलिए है क्योंकि std::shared_ptr
इम्प्लांट टाइप-इरेज़र है, जबकि std::unique_ptr
ऐसा नहीं है।
चूंकि std::shared_ptr
औजार टाइप-इरेज़र है, इसलिए यह एक और दिलचस्प संपत्ति का भी समर्थन करता है । इसे डेलेटर के प्रकार की आवश्यकता नहीं है क्योंकि क्लास के टेम्पलेट के लिए तर्क प्रकार तर्क है। उनकी घोषणाओं को देखें:
template<class T,class Deleter = std::default_delete<T> >
class unique_ptr;
जिसमें Deleter
टाइप पैरामीटर है, जबकि
template<class T>
class shared_ptr;
इसके पास नहीं है।
अब सवाल यह है कि shared_ptr
टाइप-इरेज़र क्यों लागू होता है ? ठीक है, यह ऐसा करता है, क्योंकि इसे संदर्भ-गिनती का समर्थन करना पड़ता है, और इसका समर्थन करने के लिए, इसे ढेर से मेमोरी आवंटित करना पड़ता है और चूंकि इसे करना पड़ता है भी तरह से मेमोरी आवंटित करना होता है, यह एक कदम आगे निकल जाता है और टाइप-एरेस को लागू करता है - जिसकी जरूरत है आवंटन भी। तो मूल रूप से यह सिर्फ अवसरवादी है!
टाइप-इरेज़र के कारण, std::shared_ptr
दो चीजों का समर्थन करने में सक्षम है:
void*
, फिर भी यह अभी भी विनाशकारी रूप से अपने विनाशकर्ता को सही तरीके से नष्ट करने पर वस्तुओं को हटाने में सक्षम है ।ठीक है। यह सब कैसे std::shared_ptr
काम करता है।
अब सवाल यह है कि क्या std::unique_ptr
वस्तुओं को स्टोर किया जा सकता है void*
? खैर, जवाब है, हाँ - बशर्ते आप एक उपयुक्त डिलेटर को तर्क के रूप में पारित करें। यहाँ एक ऐसा प्रदर्शन है:
int main()
{
auto deleter = [](void const * data ) {
int const * p = static_cast<int const*>(data);
std::cout << *p << " located at " << p << " is being deleted";
delete p;
};
std::unique_ptr<void, decltype(deleter)> p(new int(959), deleter);
} //p will be deleted here, both p ;-)
आउटपुट ( ऑनलाइन डेमो ):
959 located at 0x18aec20 is being deleted
आपने टिप्पणी में एक बहुत ही दिलचस्प सवाल पूछा:
मेरे मामले में मुझे एक प्रकार के उन्मूलन की आवश्यकता होगी, लेकिन यह (कुछ ढेर आवंटन की कीमत पर) भी संभव है। मूल रूप से, इसका मतलब यह है कि वास्तव में 3 प्रकार के स्मार्ट पॉइंटर के लिए एक आला स्थान है: एक विशेष स्वामित्व वाला स्मार्ट पॉइंटर प्रकार इरेज़र के साथ।
जो @Steve Jessop ने निम्नलिखित समाधान सुझाया,
मैंने वास्तव में कभी भी यह कोशिश नहीं की है, लेकिन हो सकता है कि आप एक उपयुक्त
std::function
प्रकार का उपयोग करके इसे प्राप्त कर सकेंunique_ptr
? यह मानते हुए कि वास्तव में काम करता है तो आप काम कर रहे हैं, विशेष स्वामित्व और एक प्रकार का मिटनेवाला।
इस सुझाव के बाद, मैंने इसे लागू कर दिया (हालांकि यह उपयोग नहीं करता है std::function
क्योंकि यह आवश्यक नहीं लगता है):
using unique_void_ptr = std::unique_ptr<void, void(*)(void const*)>;
template<typename T>
auto unique_void(T * ptr) -> unique_void_ptr
{
return unique_void_ptr(ptr, [](void const * data) {
T const * p = static_cast<T const*>(data);
std::cout << "{" << *p << "} located at [" << p << "] is being deleted.\n";
delete p;
});
}
int main()
{
auto p1 = unique_void(new int(959));
auto p2 = unique_void(new double(595.5));
auto p3 = unique_void(new std::string("Hello World"));
}
आउटपुट ( ऑनलाइन डेमो ):
{Hello World} located at [0x2364c60] is being deleted.
{595.5} located at [0x2364c40] is being deleted.
{959} located at [0x2364c20] is being deleted.
उम्मीद है की वो मदद करदे।
std::function
प्रकार का उपयोग करके इसे प्राप्त कर सकें unique_ptr
? यह मानते हुए कि वास्तव में काम करता है तो आप काम कर रहे हैं, विशेष स्वामित्व और एक प्रकार का मिटनेवाला।
तर्कसंगत उपयोगों में से एक के कई उपयोग-मामलों में से एक है shared_ptr
- अर्थात् जीवनकाल संकेतक या प्रहरी के रूप में।
यह मूल बढ़ावा प्रलेखन में उल्लेख किया गया था:
auto register_callback(std::function<void()> closure, std::shared_ptr<void> pv)
{
auto closure_target = { closure, std::weak_ptr<void>(pv) };
...
// store the target somewhere, and later....
}
void call_closure(closure_target target)
{
// test whether target of the closure still exists
auto lock = target.sentinel.lock();
if (lock) {
// if so, call the closure
target.closure();
}
}
closure_target
ऐसा कुछ कहाँ है:
struct closure_target {
std::function<void()> closure;
std::weak_ptr<void> sentinel;
};
कॉलर कुछ इस तरह से कॉलबैक को पंजीकृत करेगा:
struct active_object : std::enable_shared_from_this<active_object>
{
void start() {
event_emitter_.register_callback([this] { this->on_callback(); },
shared_from_this());
}
void on_callback()
{
// this is only ever called if we still exist
}
};
क्योंकि shared_ptr<X>
हमेशा के लिए परिवर्तनीय हैshared_ptr<void>
, इस घटना के प्रकार को अब अनजाने में उस वस्तु के प्रकार से अनभिज्ञ किया जा सकता है जिसे वह वापस बुला रहा है।
यह व्यवस्था ग्राहकों को क्रॉसिंग के मामलों से निपटने के दायित्व के उत्सर्जक (यदि कतार में कॉलबैक होने पर, क्रियाशील होने के दौरान कार्रवाई होने की प्रतीक्षा में?); weak_ptr<void>::lock
एक सिंक्रनाइज़ ऑपरेशन है।
std::unique_ptr<void, D>
उपयुक्त प्रदान करके यह अभी भी संभव हैD
।