क्या C++ 14 में अभी भी नए और डिलीट उपयोगी हैं?
c++11 new-operator (3)
make_unique
और
make_shared
की उपलब्धता को देखते हुए, साथ ही
unique_ptr
और
shared_ptr
विध्वंसक द्वारा स्वचालित विलोपन, C ++ 14 में
new
और
delete
लिए क्या स्थितियाँ (लीगेसी कोड का समर्थन करने के अलावा) हैं?
एकमात्र कारण जो मैं सोच सकता हूं, वह यह है कि कभी-कभी आप अपने
unique_ptr
या
shared_ptr
unique_ptr
कस्टम
unique_ptr
का उपयोग करना चाह सकते हैं।
एक कस्टम डिलेटर का उपयोग करने के लिए आपको
new
के परिणाम में सीधे स्मार्ट पॉइंटर बनाने की आवश्यकता है।
यहां तक कि यह अक्सर नहीं है, लेकिन यह व्यवहार में आता है।
इसके अलावा ऐसा लगता है कि
make_shared
/
make_unique
को बहुत सारे उपयोगों को कवर करना चाहिए।
जबकि स्मार्ट पॉइंटर्स कई मामलों में कच्चे पॉइंटर्स के लिए बेहतर हैं, फिर भी C ++ 14 में
new
/
delete
लिए बहुत सारे उपयोग-केस हैं।
यदि आपको कुछ भी लिखने की आवश्यकता है, जिसके लिए इन-प्लेस निर्माण की आवश्यकता है, उदाहरण के लिए:
- एक मेमोरी पूल
- एक आवंटनकर्ता
- एक टैग किया गया संस्करण
- एक बाइनरी को बाइनरी संदेश
आपको प्लेसमेंट
new
का उपयोग करने की आवश्यकता होगी और, संभवतः,
delete
।
उसके आसपास कोई रास्ता नहीं।
कुछ कंटेनरों के लिए जिन्हें आप लिखना चाहते हैं, आप भंडारण के लिए कच्चे पॉइंटर्स का उपयोग करना चाह सकते हैं।
यहां तक
कि मानक स्मार्ट पॉइंटर्स के लिए, आपको अभी भी
new
आवश्यकता होगी यदि आप कस्टम हटाने वालों का उपयोग करना चाहते हैं क्योंकि
make_unique
और
make_shared
लिए अनुमति नहीं देते हैं।
यह
new
कॉल करने के लिए कच्चे कॉल के बजाय
make_unique
और
make_shared
का उपयोग करने के लिए एक अपेक्षाकृत आम विकल्प है।
हालांकि, यह अनिवार्य नहीं है।
मान लें कि आप उस सम्मेलन का पालन करना चाहते हैं, तो
new
का उपयोग करने के लिए कुछ स्थान हैं।
सबसे पहले, गैर-कस्टम प्लेसमेंट
new
(मैं "गैर-कस्टम" भाग की उपेक्षा करूंगा, और बस इसे प्लेसमेंट
new
कहूंगा) मानक (गैर-प्लेसमेंट)
new
तुलना में पूरी तरह से अलग कार्ड गेम है।
इसे तार्किक रूप से मैन्युअल रूप से विध्वंसक कहते हुए जोड़ा जाता है।
मानक
new
दोनों मुक्त स्टोर से एक संसाधन प्राप्त करते हैं, और इसमें एक ऑब्जेक्ट का निर्माण करते हैं।
इसे
delete
के साथ जोड़ा गया है, जो ऑब्जेक्ट को नष्ट कर देता है और स्टोरेज को फ्री स्टोर में रीसायकल करता है।
एक मायने में, मानक
new
कॉल
new
आंतरिक रूप से प्लेसमेंट करते हैं, और मानक
delete
विध्वंसक को आंतरिक रूप से कॉल करते हैं।
प्लेसमेंट
new
वह तरीका है जिसे आप सीधे किसी स्टोरेज पर कंस्ट्रक्टर कहते हैं, और उन्नत जीवनकाल प्रबंधन कोड के लिए आवश्यक है।
यदि आप
optional
लागू कर रहे हैं, तो संरेखित भंडारण पर एक सुरक्षित
union
, या एक स्मार्ट पॉइंटर (एकीकृत भंडारण और गैर-एकीकृत जीवनकाल, जैसे
make_shared
) के साथ, आप प्लेसमेंट
new
का उपयोग कर रहे होंगे।
फिर किसी विशेष वस्तु के जीवनकाल के अंत में, आप सीधे उसके विध्वंसक को कहते हैं।
नॉन-प्लेसमेंट
new
और
delete
, प्लेसमेंट
new
और मैनुअल डिस्ट्रक्टर कॉल जोड़े में आते हैं।
कस्टम प्लेसमेंट
new
उपयोग करने का एक और कारण है।
कस्टम प्लेसमेंट
new
का उपयोग गैर-वैश्विक पूल से संसाधनों को आवंटित करने के लिए किया जा सकता है - स्कोप्ड आवंटन, या क्रॉस-प्रोसेस साझा मेमोरी पेज में आवंटन, वीडियो कार्ड साझा मेमोरी आदि में आवंटन - और अन्य उद्देश्य।
यदि आप
make_unique_from_custom
लिखना चाहते हैं जो कस्टम प्लेसमेंट नए का उपयोग करके अपनी मेमोरी आवंटित करता है, तो आपको
new
कीवर्ड का उपयोग करना होगा।
कस्टम प्लेसमेंट
new
प्लेसमेंट
new
तरह कार्य कर सकता है (इसमें वह वास्तव में संसाधनों का अधिग्रहण नहीं करता है, बल्कि संसाधन को किसी तरह पारित किया जाता है), या यह मानक
new
तरह कार्य कर सकता है (इसमें वह संसाधनों का अधिग्रहण करता है, शायद पारित किए गए तर्कों का उपयोग करके) ।
यदि कोई कस्टम प्लेसमेंट
new
फेंकता है, तो कस्टम प्लेसमेंट
delete
को कहा जाता है, इसलिए आपको यह लिखना होगा।
C ++ में आप कस्टम प्लेसमेंट
delete
नहीं कहते हैं, यह
(C ++)
आपको कॉल करता है
(r अधिभार)
।
अंत में,
make_shared
और
make_unique
अपूर्ण कार्य हैं जिसमें वे कस्टम हटाने वालों का समर्थन नहीं करते हैं।
यदि आप
make_unique_with_deleter
लिख रहे हैं, तब भी आप डेटा को आवंटित करने के लिए
make_unique
का उपयोग कर सकते हैं, और
.release()
को अपने
.release()
की देखभाल में शामिल कर सकते हैं।
यदि आपका डिलेटर अपने राज्य को
unique_ptr
या अलग आवंटन में इंगित करने के लिए बफर में
unique_ptr
, तो आपको यहां प्लेसमेंट
new
का उपयोग करना होगा।
make_shared
, क्लाइंट कोड के पास "संदर्भ गिनती स्टब" सृजन कोड तक पहुंच नहीं है।
जहाँ तक मैं बता सकता हूँ कि दोनों में आसानी से "ऑब्जेक्ट और रेफरेंस काउंटिंग ब्लॉक का संयुक्त आवंटन" और एक कस्टम डीलेटर नहीं है।
इसके अलावा,
make_shared
ऑब्जेक्ट के लिए रिसोर्स एलोकेशन (स्टोरेज) का कारण बनता है, जब तक कि यह
weak_ptr
है। यह बनी रहती है: कुछ मामलों में यह वांछनीय नहीं हो सकता है, इसलिए आप एक
shared_ptr<T>(new T(...))
करना चाहेंगे
shared_ptr<T>(new T(...))
से बचने के लिए।
कुछ मामलों में जहां आप नॉन-प्लेसमेंट
new
को कॉल करना चाहते हैं, आप
make_unique
, तो
.release()
पॉइंटर को कॉल कर सकते हैं यदि आप उस
unique_ptr
से अलग से
unique_ptr
करना चाहते हैं।
यह आपके संसाधनों के RAII कवरेज को बढ़ाता है, और इसका मतलब है कि यदि अपवाद या अन्य तर्क त्रुटियां हैं, तो आपको लीक होने की संभावना कम है।
मैंने ऊपर उल्लेख किया है कि मुझे पता नहीं था कि एक साझा पॉइंटर के साथ एक कस्टम डिलेटर का उपयोग कैसे किया जाए जो एक एकल आवंटन ब्लॉक का आसानी से उपयोग करता है। यहाँ यह कैसे करना है की एक संक्षिप्त वर्णन है:
template<class T, class D>
struct custom_delete {
std::tuple<
std::aligned_storage< sizeof(T), alignof(T) >,
D,
bool
> data;
bool bCreated() const { return std::get<2>(data); }
void markAsCreated() { std::get<2>()=true; }
D&& d()&& { return std::get<1>(std::move(data)); }
void* buff() { return &std::get<0>(data); }
T* t() { return static_cast<T*>(static_cast<void*>(buff())); }
template<class...Ts>
explicit custom_delete(Ts...&&ts):data(
{},D(std::forward<Ts>(ts)...),false
){}
custom_delete(custom_delete&&)=default;
~custom_delete() {
if (bCreated())
std::move(*this).d()(t());
}
};
template<class T, class D, class...Ts, class dD=std::decay_t<D>>
std::shared_ptr<T> make_shared_with_deleter(
D&& d,
Ts&&... ts
) {
auto internal = std::make_shared<custom_delete<T, dD>>(std::forward<D>(d));
if (!internal) return {};
T* r = new(internal->data.buff()) T(std::forward<Ts>(ts...));
internal->markAsCreated();
return { internal, r };
}
मुझे लगता है कि यह करना चाहिए। मैंने एक स्टेपल का उपयोग करके स्टेटलेस हटाने वालों को नो स्पेस का उपयोग करने की अनुमति देने का प्रयास किया, लेकिन मैंने खराब कर दिया।
लाइब्रेरी-गुणवत्ता वाले समाधान में, यदि
T::T(Ts...)
noexcept
, तो मैं
bCreated
ओवरहेड को हटा सकता हूं, क्योंकि
T
के निर्माण से पहले
custom_delete
को नष्ट होने का कोई अवसर नहीं होगा।