2012-12-14 31 views
15

मैं एक उद्देश्य-सी पृष्ठभूमि से सी ++ 11 पर आया हूं, और एक चीज जिसके साथ मैं आने के लिए संघर्ष कर रहा हूं वह सी ++ 11 के विभिन्न कैप्चरिंग अर्थशास्त्र है लैम्बडा बनाम उद्देश्य-सी "ब्लॉक"। (तुलना के लिए here देखें)।सी ++ 11 लैम्बडास को असीमित रूप से उपयोग करके, सुरक्षित रूप से

उद्देश्य-सी में, सी ++, self/this पॉइंटर को निश्चित रूप से कैप्चर किया गया है यदि आप किसी सदस्य चर का संदर्भ लेते हैं। लेकिन क्योंकि ऑब्जेक्टिव-सी में सभी वस्तुओं को प्रभावी ढंग से "साझा संकेत", सी ++ शब्दावली का उपयोग करने के लिए हैं, तो आप ऐसा कर सकते हैं:

doSomethingAsynchronously(^{ 
    someMember_ = 42; 
}); 

... और आप की गारंटी कर रहे हैं कि वस्तु जिसका सदस्य आप तक पहुँच रहे हैं ब्लॉक निष्पादित होने पर जीवित रहेगा। आपको इसके बारे में सोचना नहीं है।

// I'm assuming here that `this` derives from std::enable_shared_from_this and 
// is already owned by some shared_ptr. 
auto strongThis = shared_from_this(); 

doSomethingAsynchronously([strongThis, this] { 
    someMember_ = 42; // safe, as the lambda holds a reference to this 
         // via shared_ptr. 
}); 

यहाँ आप इस सूचक के अलावा shared_ptr कब्जा करने के लिए याद करने की आवश्यकता: सी ++ में बराबर की तरह कुछ हो रहा है। क्या इसे प्राप्त करने में कुछ कम त्रुटि-प्रवण तरीका है?

+3

* "आपको इसके बारे में सोचना नहीं है।" * इसके बारे में सोचना शुरू करें। यह बेहतर डिजाइन की ओर जाता है। – Pubby

+0

@ पब्बी लेकिन बात यह है कि, यह उन ब्लॉकों का उपयोग करने की सहजता है जो उन्हें ओब्जे-सी दुनिया में एक-शॉट असीमित कार्यों के लिए इतना उपयोगी और व्यापक बनाता है। अगर उनके पास सी ++ 11 अर्थशास्त्र था, और आपको खुद से पूछना था, "क्या यह वस्तु जीवित रहेगी, क्या यह वस्तु जीवित रहेगी, क्या यह वस्तु जीवित रहेगी ..." हर बार, मुझे लगता है कि बहुत से लोग लुभाने लगे होंगे कहो "इसे पेंच करो, मैं इसे तुल्यकालिक रूप से करूँगा।" –

+0

'इस' से साझा सूचक बनाने से यह गारंटी नहीं है कि यह अभी भी मौजूद रहेगा, जब तक कि ऑब्जेक्ट का पहले से ही किसी साझा सूचक द्वारा स्वामित्व न हो और आप इसकी प्रतिलिपि बना रहे हों। एक नया साझा पॉइंटर बनाना ('new shared_ptr (यह) 'या' make_shared (यह)') केवल डबल-डिलीट प्राप्त करने के लिए काम करेगा, जब तक कि स्मृति अन्यथा लीक न हो जाए। इसलिए, यदि आप इस बिंदु पर साझा सूचक नहीं बनाते हैं, तो आपके मामले में, यह कैसे हटाया जाता है? – Agentlien

उत्तर

6

सी ++ के संस्थापक सिद्धांतों में से एक यह है कि आप जो भी उपयोग नहीं करते हैं उसके लिए आप भुगतान नहीं करते हैं। इसका मतलब है कि इस मामले में संदर्भ है कि shared_ptr से this पर अनावश्यक है किसी भी संदर्भ गिनती ओवरहेड नहीं लेना चाहिए। इसका यह भी अर्थ है कि यह स्वचालित रूप से भी नहीं होना चाहिए उदा। enable_shared_from_this की एक विशेषता के रूप में, क्योंकि आप एक अल्गोरिदम (for_each इत्यादि) में एक अल्पकालिक लैम्ब्डा पास करना चाहते हैं, इस मामले में लैम्ब्डा अपने दायरे को पार नहीं कर सकता है।

मैं lambda-wrapper pattern को अपनाने का सुझाव देना चाहता हूं; उस मामले में यह एक बड़ी वस्तु (How to capture std::unique_ptr "by move" for a lambda in std::for_each) की move कब्जा के लिए इस्तेमाल किया है, लेकिन यह भी उतना ही this की साझा कब्जा के लिए इस्तेमाल किया जा सकता है:

template<typename T, typename F> 
class shared_this_lambda { 
    std::shared_ptr<T> t; // just for lifetime 
    F f; 
public: 
    shared_this_lambda(std::shared_ptr<T> t, F f): t(t), f(f) {} 
    template<class... Args> 
    auto operator()(Args &&...args) 
    -> decltype(this->f(std::forward<Args>(args)...)) { 
    return f(std::forward<Args>(args)...); 
    } 
}; 

template<typename T> 
struct enable_shared_this_lambda { 
    static_assert(std::is_base_of<std::enable_shared_from_this<T>, T>::value, 
    "T must inherit enable_shared_from_this<T>"); 
    template<typename F> 
    auto make_shared_this_lambda(F f) -> shared_this_lambda<T, F> { 
    return shared_this_lambda<T, F>(
     static_cast<T *>(this)->shared_from_this(), f); 
    } 
    template<typename F> 
    auto make_shared_this_lambda(F f) const -> shared_this_lambda<const T, F> { 
    return shared_this_lambda<const T, F>(
     static_cast<const T *>(this)->shared_from_this(), f); 
    } 
}; 

enable_shared_from_this के अलावा enable_shared_this_lambda इनहेरिट द्वारा उपयोग; आप तो स्पष्ट रूप से अनुरोध कर सकते हैं कि किसी भी लंबे समय तक रहा lambdas ले एक साझा this:

+0

हां, लेकिन थ्रेड शामिल होने पर आपको कुछ मेम्बर_ परमाणु या म्यूटेक्स होने की भी आवश्यकता है। –

+0

धन्यवाद, मुझे लगता है कि मैं इस पैटर्न को अपनाऊंगा। अभी भी पूरी तरह से आश्वस्त नहीं है कि यह एक अच्छी बात है कि shared_ptr लाइब्रेरी-स्तरीय फीचर है, क्योंकि यह इतना बोझिल बनाता है, लेकिन मुझे लगता है कि ब्रेक, मुझे लगता है। –

2

वास्तव में, इस समस्या का एक सही जवाब है । उत्तर shared_from_this() के साथ बाध्यकारी का सटीक प्रभाव है (जैसे कि आप इसे boost::asio::io_service के साथ करते हैं)। इसके बारे में सोचो; shared_from_this() के साथ बाध्यकारी क्या करता है? यह this को बदल देता है। तो shared_from_this() के साथ this को बदलने से आपको क्या रोकता है?

अपने उदाहरण है, जो मैं, अंतर स्पष्ट करने के लिए अद्यतन के बजाय इस बात का बाद:

auto strongThis = shared_from_this(); 

doSomethingAsynchronously([strongThis, this]() { 
    this->someMember_ = 42; //here, you're using `this`... that's wrong! 
}); 

यह करें:

auto strongThis = shared_from_this(); 

doSomethingAsynchronously([strongThis]() //notice, you're not passing `this`! 
{ 
    strongThis->someMember_ = 42;    
}); 

केवल लागत यहाँ है कि आप करने के लिए है करने वाले हैं है strongThis-> के साथ सबकुछ उपसर्ग करें। लेकिन यह करने का यह सबसे सार्थक तरीका है।