2012-03-12 16 views
6

मैं साझा पॉइंटर्स द्वारा इंगित ऑब्जेक्ट्स बनाने के लिए पहली बार boost :: make_shared का उपयोग कर रहा हूं। मुख्य रूप से क्योंकि हमारा कोड बहुत धीमा था और एकल आवंटन ने वास्तव में प्रदर्शन में सुधार करने में मदद की।बढ़ावा :: make_shared कॉलिंग (प्लेसमेंट) ऑपरेटर नया नहीं है?

कुछ मेमोरी लीक "हार्ड मैनुअल तरीके" को ठीक करने के बाद मैंने सभी प्रासंगिक कक्षाओं के लिए नए ऑपरेटरों को ओवरराइड करके एक साधारण मेमोरी लीक डिटेक्टर को लागू करने का निर्णय लिया, यह जानने के लिए कि कौन से ऑब्जेक्ट्स अभी भी हमारे आवेदन में विशिष्ट बिंदुओं पर जीवित हैं। मैंने इसे कई बार पहले लागू किया है और यह पता चला है कि मेरा कोड अब किसी ऑब्जेक्ट का पता लगाता है।

मैं लगा कि सभी मैं करना था ओवरराइड "प्लेसमेंट नया" है "सामान्य" ऑपरेटर के बजाय क्योंकि make_shared के लिए बढ़ावा वेबसाइट प्रलेखन से निम्न में से नया क्या है:

"प्रभाव: आवंटित स्मृति उपयुक्त प्रकार टी के ऑब्जेक्ट के लिए और नियुक्ति नई अभिव्यक्ति नई (PV) टी() या नए (PV) टी के माध्यम से इसे में एक वस्तु का निर्माण (std :: आगे (args) ...)। allocate_shared स्मृति आवंटित करने के लिए एक की प्रति का उपयोग करता है। यदि कोई अपवाद फेंक दिया गया है, तो प्रभाव नहीं है। "

हालांकि मेरा प्लेसमेंट नया भी नहीं कहा जा रहा है।

#include <iostream> 
using namespace std; 
#include "boost/shared_ptr.hpp" 
#include "boost/make_shared.hpp" 

class Test 
{ 
public: 
    Test() { cout << "Test::Test()" << endl; } 

    void* operator new (std::size_t size) throw (std::bad_alloc) { 
     cout << "Test new" << endl; 
     return malloc(size); 
    } 

    void* operator new (std::size_t size, const std::nothrow_t& nothrow_constant) throw() { 
     cout << "Test non-throwing new" << endl; 
     return malloc(size); 
    } 

    void* operator new (std::size_t size, void* ptr) throw() { 
     cout << "Test non-throwing placement new" << endl; 
     return malloc(size); 
    } 
}; 

void* operator new (std::size_t size) throw (std::bad_alloc) { 
    cout << "Global new" << endl; 
    return malloc(size); 
} 

int main() { 
    cout << "..." << endl; 
    boost::shared_ptr<Test> t1(boost::make_shared<Test>()); 
    cout << "..." << endl; 
    boost::shared_ptr<Test> t2(new Test()); 
    cout << "..." << endl; 

    return 0; 
} 

निम्नलिखित में से कौन उत्पादन प्रस्तुत करता है:: मैं एक छोटे से परीक्षण कार्यक्रम व्यवहार को पुनः के लिए लिखा है

... 
Global new 
Test::Test() 
... 
Test new 
Test::Test() 
Global new 
... 

मैं "परीक्षण गैर फेंकने नियुक्ति नए" उत्पादन के 3 लाइन पर उम्मीद कर रहा था। आपको क्या लगता है कि व्यवहार होना चाहिए? क्या आप सहमत हैं कि make_shared के दस्तावेज़ीकरण के अनुसार इसे मेरे टेस्ट क्लास के प्लेसमेंट के नए ऑपरेटर को कॉल करना चाहिए? या मैंने इसे गलत समझा?

मैं स्थानीय रूप से बूस्ट कार्यान्वयन की प्रतिलिपि बना सकता हूं और पाठ्यक्रम के नए ऑपरेटर को कॉल जोड़ सकता हूं। लेकिन, क्या यह उचित होगा, या यह प्लेसमेंट के इच्छित अर्थशास्त्र का उल्लंघन करेगा?

आपके समय और आपकी सहायता के लिए अग्रिम धन्यवाद।

+0

बूस्ट के को देखकर, यह वैश्विक प्लेसमेंट नया ऑपरेटर :: नया (पीवी) टी() का उपयोग कर रहा है। यही कारण है कि आपकी कक्षा स्तर की नियुक्ति नहीं कहा जा रहा है ... नए क्वालीफायर '::' को नए से पहले हटाकर, make_shared वास्तव में आपके क्लास लेवल प्लेसमेंट नए ऑपरेटर को कॉल करता है। – Gob00st

उत्तर

8

make_shared के स्रोत के रूप में देख रहे हैं, यह आपकी कक्षा द्वारा प्रदान किए गए नए ऑपरेटर की बजाय वैश्विक प्लेसमेंट new ऑपरेटर का उपयोग करता है।

::new(pv) T(); 

दुर्भाग्य (according to the standard) (ओएस एक्स पर कम से कम के रूप में), आप अपने खुद के वैश्विक नियुक्ति नए ऑपरेटर को परिभाषित नहीं कर सकते हैं। ऐसा लगता है कि allocate_shared जो आप ढूंढ रहे हैं उसके आधार पर अधिक है।

संपादित:

एक वैकल्पिक वास्तव में कौन सी क्लास के बजाय वैश्विक एक की नई नियुक्ति का उपयोग करता make_shared का एक संस्करण लिखने के लिए हो सकता है। यह केवल 10 लाइनों की कोड है, और जब तक आप the license of the original code का सम्मान करते हैं तो ठीक होना चाहिए।

+0

यह ध्यान देने योग्य है कि 'allocate_shared()' का मतलब एक बड़ा कोडबेस में कुछ कॉलों को बदलना है। –

+0

उपयोगी उत्तर के लिए धन्यवाद। मुझे नहीं पता था कि मुझे प्लेसमेंट को ओवरराइड नहीं करना था। तो यह एक समाधान में मेरे प्रयास के लिए निश्चित रूप से एक शोस्टॉपर है। मैंने पहले से ही allocate_shared विकल्प की जांच की है, लेकिन - जॉर्ज ने कहा - इसका मौजूदा कोड पर बहुत अधिक प्रभाव पड़ता है। –

4

आप प्लेसमेंट को नए (§18.4। 1.3 को प्रतिस्थापित नहीं कर सकते हैं, उदाहरण के लिए this question देखें), इसलिए दिया गया आउटपुट ठीक लगता है।

बूस्ट हेडर को संशोधित करने के विकल्प के रूप में, आप वालग्रींड जैसे बाहरी उपकरणों को देख सकते हैं।

3

आपका operator new अपने विशेष प्रकार के लिए लागू किया केवल इस तरह के Test *p = new Test; के रूप में भाव जिस पर अपने प्रकार के तत्वों गतिशील new साथ आवंटित किया जाता है, पर इस्तेमाल किया जाएगा। अब make_shared नहीं गतिशील अपने प्रकार का ऑब्जेक्ट आवंटित है, लेकिन करता है बल्कि एक बफर कि साझा गिनती के लिए पर्याप्त जानकारी नहीं है और अपने वस्तु (जो काउंटर, Deleter और कुछ अतिरिक्त बिट और टुकड़े भी शामिल है) आयोजित करता है।

यह आपके ऑब्जेक्ट के निर्माता को कॉल करने के लिए प्लेसमेंट-नई का उपयोग करता है। ध्यान दें कि प्लेसमेंट नया इस मामले में स्मृति आवंटित नहीं कर रहा है, यह पहले से आवंटित स्मृति के ब्लॉक पर कन्स्ट्रक्टर को कॉल करने के लिए सी ++ में केवल मजाकिया वाक्यविन्यास है। यह वास्तव में भ्रम का स्रोत हो सकता है, new अभिव्यक्ति के रूप में, आपके operator new और नियुक्ति-नए नाम साझा करने के लिए तीन अलग-अलग अवधारणाएं हैं।

+0

इसके बावजूद, नियुक्ति-नए के लिए भी प्रति-वर्ग अधिभार प्रदान करना अभी भी संभव है। असामान्य शायद, लेकिन फिर, सी ++ * का कौन सा हिस्सा असामान्य नहीं है :-) घर ले जाने का मुख्य बिंदु यह है कि 'std :: आवंटक' किसी भी प्रति-वर्ग आवंटन कार्यों का उपयोग नहीं करता है, बल्कि केवल वैश्विक ' :: new'। –

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