2016-09-02 9 views
64

प्रश्न वास्तव में शीर्षक में फिट बैठता है: मुझे यह जानकर उत्सुकता है कि इस अंतर के तकनीकी कारण क्या हैं, बल्कि तर्क भी?साझा_ptr <void> कानूनी क्यों है, जबकि unique_ptr <void> खराब गठित है?

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

उत्तर

73

यह इसलिए क्योंकि std::shared_ptr लागू करता है प्रकार विलोपन, जबकि std::unique_ptr नहीं करता है।


std::shared_ptr के बाद से लागू करता है प्रकार विलोपन, यह भी एक और दिलचस्प संपत्ति, अर्थात समर्थन करता है। यह क्लास टेम्पलेट में टेम्पलेट प्रकार तर्क के रूप में deleter के प्रकार की आवश्यकता है। उनकी घोषणाओं को देखो:

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 ;-) 

आउटपुट (online demo):

मेरे मामले में मैं की आवश्यकता होगी:

959 located at 0x18aec20 is being deleted 

आप टिप्पणी में एक बहुत ही दिलचस्प प्रश्न पूछा यहाँ ऐसे ही एक प्रदर्शन है डिलीटर मिटाने वाला एक प्रकार, लेकिन यह भी संभव है (कुछ ढेर आवंटन की लागत पर)। असल में, इसका मतलब यह है कि वास्तव में तीसरे प्रकार के स्मार्ट पॉइंटर के लिए एक विशिष्ट स्थान है: टाइप एरर के साथ एक विशेष स्वामित्व स्मार्ट पॉइंटर।

जो @Steve Jessop निम्नलिखित समाधान,

मैं वास्तव में इस की कोशिश की कभी नहीं किया है सुझाव दिया है, लेकिन हो सकता है आप है कि एक उचित std::functionunique_ptr साथ Deleter प्रकार के रूप में उपयोग करके प्राप्त कर सकते थे? मान लीजिए कि वास्तव में काम करता है तो आप कर चुके हैं, अनन्य स्वामित्व और एक प्रकार के मिटाए गए डिलीटर।

इस सुझाव के बाद, मैं इस लागू किया,

using deleter_t = std::function<void(void *)>; 
using unique_void_ptr = std::unique_ptr<void, deleter_t>; 

template<typename T> 
auto deleter(void const * data) -> void 
{ 
    T const * p = static_cast<T const*>(data); 
    std::cout << "{" << *p << "} located at [" << p << "] is being deleted.\n"; 
    delete p; 
} 

template<typename T> 
auto unique_void(T * ptr) -> unique_void_ptr 
{ 
    return unique_void_ptr(ptr, &deleter<T>); 
} 

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")); 
} 

आउटपुट (online demo):

{Hello World} located at [0x2364c60] is being deleted. 
{595.5} located at [0x2364c40] is being deleted. 
{959} located at [0x2364c20] is being deleted. 

आशा में मदद करता है।

+9

अच्छा जवाब, +1। लेकिन आप स्पष्ट रूप से उल्लेख कर सकते हैं कि एक 'std :: unique_ptr ' उपयुक्त 'डी' प्रदान करके अभी भी संभव है। – Angew

+1

@Angrew: अच्छा है, आपको असली अंतर्निहित प्रश्न मिला जो मेरे प्रश्न में नहीं लिखा गया था;) –

+0

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

5

तर्कसंगतों में से एक shared_ptr के कई उपयोग-मामलों में से एक है - अर्थात् जीवनभर सूचक या सेंटीनेल के रूप में।

यह मूल बढ़ावा दस्तावेज में उल्लेख किया गया था:

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 
    } 
}; 
:

struct closure_target { 
    std::function<void()> closure; 
    std::weak_ptr<void> sentinel; 
}; 

फोन करने वाले इस तरह एक कॉलबैक कुछ रजिस्टर होगा:

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 कहाँ कुछ इस तरह है

क्योंकि shared_ptr<X> हमेशा सह है shared_ptr<void> पर परिवर्तनीय, event_emitter अब उस ऑब्जेक्ट के प्रकार से अनजान हो सकता है जिसे वह वापस बुला रहा है।

यह व्यवस्था ग्राहकों को क्रॉसिंग मामलों को संभालने के दायित्व के इमिटर को जारी करती है (क्या होगा यदि कतार में कॉलबैक, सक्रिय_बोजेक्ट दूर होने पर कार्रवाई करने का इंतजार कर रहा हो?), और इसका भी अर्थ है कि सिंक्रनाइज़ करने की कोई आवश्यकता नहीं है सदस्यता खत्म करने। weak_ptr<void>::lock एक सिंक्रनाइज़ ऑपरेशन है।

संबंधित मुद्दे