क्या 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 को नष्ट होने का कोई अवसर नहीं होगा।






dynamic-memory-allocation