2008-08-18 23 views
30

क्या एरे के लिए इसका उपयोग करते समय वास्तव में पोर्टेबल कोड में प्लेसमेंट का उपयोग करना संभव है?क्या पोर्टेबल तरीके से सरणी के लिए नया प्लेसमेंट इस्तेमाल किया जा सकता है?

ऐसा प्रतीत होता है कि जो सूचक आप नए से वापस प्राप्त करते हैं [] हमेशा आपके द्वारा दर्ज किए गए पते के समान नहीं होता है (5.3.4, मानक में नोट 12 यह पुष्टि करता है कि यह सही है), लेकिन मैं ' टी यह देखने के लिए कि यदि आप इस मामले में जाने के लिए सरणी के लिए एक बफर आवंटित कर सकते हैं तो देखें।

निम्नलिखित उदाहरण समस्या दिखाता है। दृश्य स्टूडियो के साथ संकलित, इस उदाहरण स्मृति भ्रष्टाचार में परिणाम:

#include <new> 
#include <stdio.h> 

class A 
{ 
    public: 

    A() : data(0) {} 
    virtual ~A() {} 
    int data; 
}; 

int main() 
{ 
    const int NUMELEMENTS=20; 

    char *pBuffer = new char[NUMELEMENTS*sizeof(A)]; 
    A *pA = new(pBuffer) A[NUMELEMENTS]; 

    // With VC++, pA will be four bytes higher than pBuffer 
    printf("Buffer address: %x, Array address: %x\n", pBuffer, pA); 

    // Debug runtime will assert here due to heap corruption 
    delete[] pBuffer; 

    return 0; 
} 

स्मृति को देखते हुए, संकलक उस में वस्तुओं की संख्या की गिनती स्टोर करने के लिए बफर के पहले चार बाइट्स का उपयोग किया जा रहा है। इसका मतलब है कि क्योंकि बफर केवल sizeof(A)*NUMELEMENTS बड़ा है, सरणी में अंतिम तत्व आवंटित ढेर में लिखा गया है।

तो सवाल यह है कि आप यह पता लगा सकते हैं कि नियुक्ति नया [] सुरक्षित रूप से उपयोग करने के लिए आपका कार्यान्वयन कितना अतिरिक्त ओवरहेड चाहता है? आदर्श रूप में, मुझे एक ऐसी तकनीक की आवश्यकता है जो विभिन्न कंपाइलरों के बीच पोर्टेबल हो। ध्यान दें कि, कम से कम वीसी के मामले में, ओवरहेड अलग-अलग वर्गों के लिए अलग दिखता है। उदाहरण के लिए, यदि मैं उदाहरण में वर्चुअल विनाशक को हटा देता हूं, तो नया [] से लौटाया गया पता उसी पते जैसा है जो मैं पास करता हूं।

+0

आह श्राप।मैंने आपके प्रश्न का एक डुप्लिकेट किया :([ऐरे प्लेसमेंट-नए को बफर में अनिर्दिष्ट ओवरहेड की आवश्यकता है?] (Http://stackoverflow.com/questions/8720425/array-placement- new-requires-unspecified-overhead-in-the -बफर) –

+0

हम्म ... यदि आप वर्चुअल विनाशक को हटाते हैं तो ओवरहेड गायब हो जाता है, जो यह सुझाव देगा कि ओवरहेड कक्षा के vtable से या आरटीटीआई के VStudio के कार्यान्वयन से होने की संभावना है। –

+0

या कम से कम, ओवरहेड है। यह भी संभव है कि ओवरहेड का उपयोग केवल तभी किया जाता है जब कक्षा में एक गैर-तुच्छ विनाशक होता है। –

उत्तर

22

व्यक्तिगत रूप से मैं सरणी पर नया प्लेसमेंट का उपयोग न करने के विकल्प के साथ जाऊंगा और इसके बजाय सरणी में प्रत्येक आइटम पर अलग-अलग प्लेसमेंट का उपयोग करूँगा। उदाहरण के लिए:

int main(int argc, char* argv[]) 
{ 
    const int NUMELEMENTS=20; 

    char *pBuffer = new char[NUMELEMENTS*sizeof(A)]; 
    A *pA = (A*)pBuffer; 

    for(int i = 0; i < NUMELEMENTS; ++i) 
    { 
    pA[i] = new (pA + i) A(); 
    } 

    printf("Buffer address: %x, Array address: %x\n", pBuffer, pA); 

    // dont forget to destroy! 
    for(int i = 0; i < NUMELEMENTS; ++i) 
    { 
    pA[i].~A(); 
    }  

    delete[] pBuffer; 

    return 0; 
} 

विधि आप उपयोग के बावजूद, सुनिश्चित करें कि आप मैन्युअल रूप से, सरणी में उन वस्तुओं में से प्रत्येक को नष्ट इससे पहले कि आप pBuffer हटाना आप लीक लग सकती है के रूप में करते हैं;)

नोट : मैंने इसे संकलित नहीं किया है, लेकिन मुझे लगता है कि इसे काम करना चाहिए (मैं ऐसी मशीन पर हूं जिसमें सी ++ कंपाइलर स्थापित नहीं है)। यह अभी भी बिंदु इंगित करता है :) उम्मीद है कि यह किसी भी तरह से मदद करता है!


संपादित करें:

कारण यह तत्वों की संख्या का ट्रैक रखने की जरूरत है तो यह है कि यह उन के माध्यम से पुनरावृति सकता है जब आप सरणी पर हटाने के फोन और यह सुनिश्चित करें विनाशकर्ता में से प्रत्येक पर कहा जाता है बनाने के वस्तुओं अगर यह नहीं जानता कि कितने हैं तो यह ऐसा करने में सक्षम नहीं होगा।

+1

वीसी ++ एक खराब कंपाइलर है। डिफ़ॉल्ट प्लेसमेंट नई सरणी उर्फ ​​'नया (शून्य *) प्रकार [n]' 'n' ऑब्जेक्ट्स के इन-प्लेस निर्माण करता है 'टाइप' का। प्रदान किया गया पॉइंटर मिलान करने के लिए ठीक से गठबंधन किया जाना चाहिए 'alignof (प्रकार)' (नोट: 'आकार (प्रकार) 'पैडिंग के कारण' alignof (प्रकार)' का एक बहु है। क्योंकि आपको आमतौर पर सरणी की लंबाई के चारों ओर ले जाना होता है, इसलिए इसे सरणी के अंदर स्टोर करने की कोई वास्तविक आवश्यकता नहीं होती है, क्योंकि आप किसी भी फॉर-लूप के साथ इसे नष्ट करने जा रहे हैं (कोई प्लेसमेंट ऑपरेटरों को हटा नहीं सकता है)। – bit2shift

+1

@ bit2shift सी ++ मानक स्पष्ट रूप से बताता है कि 'नया [] 'आवंटित स्मृति को पैड करता है, हालांकि यह 0 बाइट्स के पैडिंग की अनुमति देता है। (अनुभाग "expr.new" देखें, विशेष रूप से उदाहरण (14.3) और (14.4) और उनके नीचे स्पष्टीकरण।) तो, इस संबंध में, यह वास्तव में मानक-अनुरूप है। \ [यदि आपके पास C++ 14 मानक के अंतिम संस्करण की एक प्रति नहीं है, तो पृष्ठ 133 देखें [यहां] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers /2014/n4296.pdf)। \] –

+1

@ जस्टिनटाइम यह एक दोष है कोई भी आंखों पर बल्लेबाजी नहीं करता है। – bit2shift

1

मुझे लगता है कि जीसीसी एमएसवीसी के समान ही काम करता है, लेकिन निश्चित रूप से यह ' इसे "पोर्टेबल" बनाओ।

मुझे लगता है कि आप समस्या को हल करने कर सकते हैं जब NUMELEMENTS वास्तव में एक संकलन समय स्थिर है, इसलिए जैसे:

typedef A Arr[NUMELEMENTS];

A* p = new (buffer) Arr;

यह अदिश नियुक्ति नए प्रयोग करना चाहिए।

+1

यह नहीं है। 'ऑपरेटर नया() 'बनाम' ऑपरेटर नया []() 'इस पर निर्भर करता है कि कोई सरणी है या नहीं स्रोत कोड में '[]' शामिल थे, नहीं। –

1

एक प्लेसमेंट के लिए आकार की गणना करने के लिए आप एक तत्व का उपयोग कैसे करेंगे- नए, सरणी के लिए आवश्यक आकार की गणना करने के लिए उन तत्वों की एक सरणी का उपयोग करें।

यदि आपको अन्य गणनाओं के लिए आकार की आवश्यकता होती है जहां तत्वों की संख्या ज्ञात नहीं हो सकती है तो आप आकार (ए [1]) का उपयोग कर सकते हैं और अपनी आवश्यक तत्व गणना से गुणा कर सकते हैं।

ई।जी

char *pBuffer = new char[ sizeof(A[NUMELEMENTS]) ]; 
A *pA = (A*)pBuffer; 

for(int i = 0; i < NUMELEMENTS; ++i) 
{ 
    pA[i] = new (pA + i) A(); 
} 
+0

बिंदु यह है कि एमएसवीसी स्पष्ट रूप से 'new []' के मामले में 'आकार (ए [NUMELEMENTS]) के मूल्य से परे अतिरिक्त स्थान की आवश्यकता है। 'आकार (ए [एन])' सामान्य रूप से 'एन * आकार (एन)' होने जा रहा है और यह अतिरिक्त आवश्यक स्थान को प्रतिबिंबित नहीं करेगा। – BeeOnRope

1

उत्तर के लिए धन्यवाद। सरणी में प्रत्येक आइटम के लिए नया प्लेसमेंट का उपयोग करना उस समाधान का उपयोग करता था जब मैं इसमें भाग गया था (क्षमा करें, उस प्रश्न में उल्लेख किया जाना चाहिए था)। मैंने महसूस किया कि प्लेसमेंट के साथ ऐसा करने के बारे में कुछ ऐसा होना चाहिए []। जैसा कि है, ऐसा लगता है कि प्लेसमेंट नया [] अनिवार्य रूप से अनुपयोगी है, मानक को संकलक को सरणी में एक अतिरिक्त अनिर्दिष्ट ओवरहेड जोड़ने की इजाजत देता है। मैं नहीं देखता कि आप इसे सुरक्षित रूप से और पोर्टेबल तरीके से कैसे इस्तेमाल कर सकते हैं।

मैं भी वास्तव में स्पष्ट नहीं हूं कि इसे अतिरिक्त डेटा की आवश्यकता क्यों है, क्योंकि आप किसी भी तरह से सरणी पर हटाएं [] को कॉल नहीं करेंगे, इसलिए मुझे पूरी तरह से नहीं पता कि इसे क्यों पता होना चाहिए कि इसमें कितनी वस्तुएं हैं ।

2

@James

मैं भी नहीं वास्तव में स्पष्ट कारण है कि यह अतिरिक्त डेटा की जरूरत है, के रूप में हटाना [] सरणी वैसे भी पर कॉल नहीं होता, तो मैं पूरी तरह से नहीं दिख रहा है कारण है कि यह जरूरत है यह जानने के लिए कि इसमें कितने आइटम हैं।

कुछ विचार देने के बाद, मैं आपसे सहमत हूं। प्लेसमेंट को तत्वों की संख्या को स्टोर करने की आवश्यकता होने का कोई कारण नहीं है, क्योंकि कोई प्लेसमेंट डिलीट नहीं है। चूंकि कोई प्लेसमेंट डिलीट नहीं है, इसलिए तत्वों की संख्या को स्टोर करने के लिए प्लेसमेंट नए के लिए कोई कारण नहीं है।

मैंने एक विनाशक के साथ एक वर्ग का उपयोग करके, मैक पर जीसीसी के साथ इसका परीक्षण किया। मेरे सिस्टम पर, प्लेसमेंट नया सूचक बदल रहा था। इससे मुझे आश्चर्य होता है कि यह एक वीसी ++ मुद्दा है, और क्या यह मानक का उल्लंघन कर सकता है (मानक विशेष रूप से इसे संबोधित नहीं करता है, जहां तक ​​मुझे मिल सकता है)।

+0

मैंने क्लैंग 3.7.0 और जीसीसी 5.3.0 दोनों के साथ परीक्षण किया, दोनों '-std = C++ 14' और' colidu] पर '-pedantic'' (http://coliru.stacked-crooked.com) के साथ। न तो एक उपरांत के अस्तित्व को दिखाया, विशेष रूप से गैर-विनाशकारी विनाशकों वाले वर्गों के साथ। तो, मैं सोच रहा हूं, यह एक और उदाहरण है कि विज़ुअल सी ++ कितना खराब कंपाइलर है। – bit2shift

4

@Derek

5.3.4, खंड 12 सरणी आवंटन भूमि के ऊपर और, जब तक कि मैं इसे ग़लत व्याख्या कर रहा हूँ, यह मेरे लिए जो बताते हैं कि यह मान्य है संकलक नियुक्ति नई पर इसे जोड़ने के लिए के लिए लगता है के बारे में वार्ता साथ ही:

यह ओवरहेड सभी सरणी नई-भाव में लागू किया जा सकता है, पुस्तकालय समारोह ऑपरेटर नई [] संदर्भित और अन्य प्लेसमेंट आवंटन कार्य (std :: size_t * शून्य,) भी शामिल हैं। ओवरहेड की मात्रा नए से दूसरे के एक आमंत्रण से भिन्न हो सकती है।

उसने कहा, मुझे लगता है कि वीसी एकमात्र कंपाइलर था जिसने मुझे इसके बारे में परेशानी दी, जीसीसी, कोडवायरियर और प्रोडीजी। हालांकि, मुझे यकीन करने के लिए फिर से जांचना होगा।

+0

यदि वीसी एकमात्र कंपाइलर अतिरिक्त जगह जोड़ रहा था, तो मुझे आश्चर्य होगा कि मुझे लगता है कि सभी कंपाइलर्स वहां कॉल करने के लिए विनाशकों की संख्या को स्टोर करेंगे। इसे रखने के लिए कोई अन्य तार्किक जगह नहीं है। –

+1

@MooingDuck उपर्युक्त "सरणी लंबाई" का उपयोग करने के लिए कोई प्लेसमेंट डिलीट ऑपरेटर नहीं है। वास्तव में, आपको 'नए (सूचक) प्रकार [लंबाई] 'के साथ ** निर्मित ** सरणी के लिए मैन्युफैक्चरर्स को मैन्युअल रूप से कॉल करना होगा। वीसी एक खराब कंपाइलर है, हर किसी को यह पता होना चाहिए। – bit2shift

3

प्लेसमेंट नया स्वयं पोर्टेबल है, लेकिन आपके द्वारा किए गए अनुमानों के बारे में आप स्मृति के निर्दिष्ट ब्लॉक के साथ क्या कर रहे हैं पोर्टेबल नहीं हैं। जैसा कि पहले कहा गया था, अगर आप एक कंपाइलर थे और उन्हें स्मृति का एक हिस्सा दिया गया था, तो आप कैसे जानते होंगे कि कैसे एक सरणी आवंटित करें और प्रत्येक तत्व को ठीक से नष्ट कर दें यदि आपके पास कोई सूचक था? (ऑपरेटर के इंटरफेस को नष्ट [] देखें।)

संपादित करें:

और वास्तव में एक प्लेसमेंट वहाँ हटाएं होता है, केवल यह तभी होता है जब एक निर्माता है, जबकि [] नियुक्ति नए के साथ एक सरणी का आवंटन एक अपवाद फेंकता है कहा जाता है।

चाहे नए [] को वास्तव में तत्वों की संख्या का ट्रैक रखने की आवश्यकता है, किसी भी तरह मानक के लिए छोड़ दिया गया है, जो इसे कंपाइलर तक छोड़ देता है। दुर्भाग्य से, इस मामले में।

+1

यह कभी भी "पोर्टेबल" कैसे हो सकता है यदि यह एक मनमाने ढंग से बड़ी मात्रा में भंडारण को ओवरराइट करता है? आप कभी भी इसे सुरक्षित रूप से कॉल नहीं कर सकते क्योंकि आप कभी नहीं जानते कि यह कितना लिखने जा रहा है। – BeeOnRope

+0

'हटाएं []' अभिव्यक्ति को जानने की आवश्यकता है, लेकिन इसका उपयोग केवल 'नई []' अभिव्यक्ति के परिणामस्वरूप हीप आवंटन (या तो फेंकना या 'नोट्रो') के परिणामस्वरूप किया जा सकता है। नियुक्ति के लिए 'नया []' यह वास्तव में अनावश्यक है।वीसी के व्यवहार के लिए मेरे पास एकमात्र स्पष्टीकरण है कि 'नया []' किसी भी तरह नियुक्ति 'नए [] 'के संदर्भ में लागू किया गया है और तत्वों की संख्या को स्वयं संग्रहित नहीं करता है। –