2016-01-08 8 views
5

मैं बस की खोज की है std::shared_ptr के "निर्माता अलियासिंग" और पाते हैं अपने आप पूछ "क्यों std नहीं है :: unique_ptr एक एक?std :: shared_ptr जैसे sti :: unique_ptr का कोई एलियासिंग कन्स्ट्रक्टर क्यों नहीं है?

यही है, अगर आप ऐसा कर सकते हैं कि एक Foo आवंटित करना चाहते इसी राशि एक समारोह है कि पूरी तरह से Foo के जीवनकाल का प्रबंधन करना चाहिए करने के लिए अपने Bar सदस्य पारित यह अच्छा नहीं होगा ऐसा करने के लिए सक्षम होने के लिए?

#include <memory> 

struct B {} 
struct A { 
    B b; 
} 

void f(std::unique_ptr<B> b); 

std::unique_ptr<A> a = std::make_unique<A>(); 
std::unique_ptr<B> b { std::move(a), &(a->b) }; // a now invalid. 
f(std::move(b)); // f now responsible for deleting the A. 

यह एसटीडी के साथ काम करता :: shared_ptr (http://ideone.com/pDK1bc)

#include <iostream> 
#include <memory> 
#include <string> 

struct B { 
    std::string s; 
}; 
struct A { 
    B b; 
    A(std::string s) : b{s} {}; 
    ~A() { std::cout << "A deleted." << std::endl; } 
}; 

void f(std::shared_ptr<B> b) { 
    std::cout << "in f, b->s = " << b->s << " (use_count=" << b.use_count() << ")" << std::endl; 
} 

int main() { 
    std::shared_ptr<A> a = std::make_shared<A>("hello"); 
    std::shared_ptr<B> b { a, &(a->b) }; 
    a.reset(); // a now invalid. 
    std::cout << "before f, b->s = " << b->s << " (use_count=" << b.use_count() << ")" << std::endl; 
    f(std::move(b)); // f now responsible for deleting the A. 
    std::cout << "after f" << std::endl; 
    return 0; 
} 

उम्मीद outputs

before f, b->s = hello (use_count=1) 
in f, b->s = hello (use_count=1) 
A deleted. 
after f 

वहाँ एक तार्किक कारण है कि ऐसी बात शामिल नहीं किया गया है? और/या, को हटाए गए कस्टम डिलीटर के साथ unique_ptr<B> के साथ अनुकरण करना एक बुरा विचार है?

उत्तर

8

मेरा मानना ​​है कि "समस्या" यह है कि std::shared_ptr के विपरीत, std::unique_ptr का डिलीटर टाइप-मिटा नहीं गया है। std::unique_ptr<T> का डिफ़ॉल्ट डिलीटर (जिसमें शून्य आकार है, जो कि टाइप में स्वयं को एक साधारण रूप से दृश्यमान डिफ़ॉल्ट प्रकार पैरामीटर के रूप में एन्कोड किया गया है) बस [](T * p){ delete p; } है। लेकिन यह स्पष्ट है कि std::unique_ptr<B> जो std::make_unique<B> के माध्यम से बनाया गया था और BA ऑब्जेक्ट के सदस्य को इंगित करके बनाया गया था, वही डिलीटर नहीं हो सकता है। बाद के मामले के लिए हटाए जाने वाले को मूल A * पॉइंटर वापस प्राप्त करने के लिए कुछ पॉइंटर अंकगणित करना होगा। उन दो deleters केवल एक ही प्रकार हो सकता है अगर दोनों मूल वस्तु के लिए ऑफसेट या एक आंतरिक सूचक स्टोर करेंगे। और उसके पास शून्य आकार नहीं होगा। std::unique_ptr को new और delete मैन्युअल रूप से करने की तुलना में शून्य ओवरहेड रखने के लिए डिज़ाइन किया गया था, जो एक अच्छी बात है। मुझे आपके खुद के डेलेटर्स का उपयोग करने से कोई तत्काल कमी दिखाई नहीं देती है जो उस अतिरिक्त सूचक को स्टोर करते हैं, हालांकि मुझे अभी भी एक उपयोग-मामले में आना होगा जहां मुझे यह उपयोगी लगेगा।

5

shared_ptr में संदर्भ गिनती संदर्भ है। इसके संदर्भ गिनती ब्लॉक में यह एक स्पष्ट डिलीटर भी संग्रहीत करता है (क्योंकि यदि आप ढेर पर संग्रहीत कर रहे हैं, तो कुछ और बाइट्स क्या हैं?)

यही कारण है कि बिना किसी प्रकार के shared_ptr को व्युत्पन्न प्रकारों को हटाया जा सकता है एक आभासी dtor।

unique_ptr, दूसरी तरफ, उदाहरण में इसके डिलीटर को स्टोर करता है, और डिफ़ॉल्ट डिलीटर स्टेटलेस - 0 बाइट्स का उपयोग किया जाता है। यह मेमोरी उपयोग के मामले में कच्चे सूचक पर unique_ptr शून्य ओवरहेड बनाता है।

एक स्टेटलेस डिलीटर कुछ और हटाने के लिए याद नहीं कर सकता है।

आप unique_ptr पर एक स्टेटस डिलीटर जोड़ सकते हैं जो एलियासिंग का समर्थन करता है, लेकिन फिर आपको मैन्युअल रूप से उपनाम करना होगा। रचनाकारों में से एक पॉइंटर और एक डिलीटर दोनों लेता है।

+0

और फिर जब डिलीटर के प्रकार का हिस्सा है, तो यह सादे 'unique_ptr 'के साथ अच्छी तरह से इंटरऑप नहीं करता है जब तक कि आप डिलीटर को मिटाने के प्रकार को टाइप न करें, जो कि अधिक अतिरिक्त ओवरहेड है। –

+0

@ टी.सी .: यहां तक ​​कि अगर आप डिलीटर मिटाते हैं, तो यह 'unique_ptr <टी, default_deleter >' के साथ अच्छी तरह से इंटरऑप नहीं करता है। कम से कम, केवल एक दिशा में रूपांतरण संभव है। –

+0

@ बीन सिद्धांत में दोनों तरीकों से, जब तक विफलता की अनुमति है। – Yakk

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