Share_ptr <void> कानूनी क्यों है, जबकि unique_ptr <void> बीमार है?


100

सवाल वास्तव में शीर्षक में फिट बैठता है: मैं यह जानने के लिए उत्सुक हूं कि इस अंतर का तकनीकी कारण क्या है, लेकिन यह भी तर्क है?

std::shared_ptr<void> sharedToVoid; // legal;
std::unique_ptr<void> uniqueToVoid; // ill-formed;

जवाबों:


118

ऐसा इसलिए है क्योंकि 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.

उम्मीद है की वो मदद करदे।


13
अच्छा उत्तर, +1। लेकिन आप यह स्पष्ट रूप से उल्लेख करके बेहतर बना सकते हैं कि std::unique_ptr<void, D>उपयुक्त प्रदान करके यह अभी भी संभव है D
Angew को अब SO

1
@ भाषा: एक अच्छा, आपको वास्तविक अंतर्निहित प्रश्न मिला जो मेरे प्रश्न में नहीं लिखा था;)
विज्ञापन एन

@ नवाज़: धन्यवाद। मेरे मामले में मुझे एक प्रकार के उन्मूलन की आवश्यकता होगी, लेकिन यह (कुछ ढेर आवंटन की कीमत पर) भी संभव है। असल में, क्या इसका मतलब यह है कि वास्तव में 3 प्रकार के स्मार्ट पॉइंटर के लिए एक आला स्थान है: प्रकार के क्षरण के साथ एक विशेष स्वामित्व वाला स्मार्ट पॉइंटर?
Ad N

8
@ विज्ञापन: मैंने वास्तव में कभी भी यह कोशिश नहीं की है, लेकिन हो सकता है कि आप एक उपयुक्त std::functionप्रकार का उपयोग करके इसे प्राप्त कर सकें unique_ptr? यह मानते हुए कि वास्तव में काम करता है तो आप काम कर रहे हैं, विशेष स्वामित्व और एक प्रकार का मिटनेवाला।
स्टीव जेसोप

व्याकरण नाइट: "एक्स क्रिया वाई क्यों?" होना चाहिए "क्यों एक्स क्रिया वाई करता है ?" अंग्रेजी में।
zwol

7

तर्कसंगत उपयोगों में से एक के कई उपयोग-मामलों में से एक है 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एक सिंक्रनाइज़ ऑपरेशन है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.