2014-12-18 12 views
6

मैं कुछ कोड में आया हूं जो मुझे डराता है। मूलतः यह इस पद्धति का अनुसरण करता:सी ++ प्लेसमेंट नए अपरिभाषित व्यवहार का उपयोग कर ऑब्जेक्ट का निर्माण दो बार कर रहा है?

class Foo 
{ 
    public: 
    //default constructor 
    Foo(): x(0), ptr(nullptr) 
    { 
     //do nothing 
    } 

    //more interesting constructor 
    Foo(FooInitialiser& init): x(0), ptr(nullptr) 
    { 
     x = init.getX(); 
     ptr = new int; 
    } 
    ~Foo() 
    { 
     delete ptr; 
    } 

    private: 
    int x; 
    int* ptr; 
}; 


void someFunction(FooInitialiser initialiser) 
{ 
    int numFoos = MAGIC_NUMBER; 
    Foo* fooArray = new Foo[numFoos]; //allocate an array of default constructed Foo's 

    for(int i = 0; i < numFoos; ++i) 
    { 
     new(fooArray+ i) Foo(initialiser); //use placement new to initialise 
    } 

    //... do stuff 

    delete[] fooArray; 
} 

इस कोड को साल के लिए कोड बेस में किया गया है और यह प्रतीत होता है एक समस्या कभी नहीं हुई है। यह स्पष्ट रूप से एक बुरा विचार है क्योंकि कोई दूसरा निर्माण करने की अपेक्षा न करने के लिए डिफ़ॉल्ट कन्स्ट्रक्टर बदल सकता है। बस समकक्ष प्रारंभिक विधि के साथ दूसरे कन्स्ट्रक्टर को प्रतिस्थापित करने के लिए समझदार चीज़ लगती है। जैसे।

void Foo::initialise(FooInitialiser& init) 
{ 
    x = init.getX(); 
    ptr = new int; 
} 

हालांकि अभी भी संभव संसाधन लीक करने के लिए विषय, कम से कम एक रक्षात्मक प्रोग्रामर एक सामान्य विधि में पूर्व आवंटन की जांच करने के बारे में सोच सकता है।

मेरा प्रश्न है:

दो बार निर्माण कर रही है यह वास्तव में अपरिभाषित व्यवहार की तरह/मानक या बस सिर्फ एक बुरा विचार द्वारा गैरकानूनी घोषित? यदि अपरिभाषित व्यवहार आप मानक को देखने के लिए सही स्थान पर उद्धृत या इंगित कर सकते हैं?

+0

क्या आपने इस कोड पर valgrind करने की कोशिश की? – zoska

+0

मुझे लगता है कि मुख्य समस्या यह है कि 'Foo' तीन के नियम का पालन नहीं करता है - डिफ़ॉल्ट प्रति-ctor और कॉपी-असाइनमेंट-ऑपरेटर' Foo :: ptr' के साथ सही चीज़ नहीं करेगा। – cdhowie

+0

@cdhowie शायद हमें अन्य लोगों के कोड के बारे में सबसे बुरा नहीं मानना ​​चाहिए। मुझे लगता है कि ओपी ने उस कोड को काट दिया जो सवाल पूछने के लिए जरूरी नहीं था। – anatolyg

उत्तर

10

आम तौर पर, इस तरह से नए प्लेसमेंट के साथ काम करना एक अच्छा विचार नहीं है। पहले नए से प्रारंभकर्ता को कॉल करना, या प्लेसमेंट नए के बजाय प्रारंभकर्ता को कॉल करना आपके द्वारा प्रदान किए गए कोड से बेहतर फ़ॉर्म माना जाता है।

हालांकि, इस मामले में, किसी मौजूदा ऑब्जेक्ट पर नए प्लेसमेंट को कॉल करने का व्यवहार अच्छी तरह से परिभाषित किया गया है।

एक कार्यक्रम भंडारण जो वस्तु पर है या स्पष्ट रूप से एक गैर तुच्छ नाशक के साथ एक कक्षा प्रकार का ऑब्जेक्ट के लिए नाशक फोन करके पुन: उपयोग से किसी भी वस्तु का जीवन समाप्त हो सकता है। एक गैर-तुच्छ विनाशक के साथ कक्षा प्रकार के ऑब्जेक्ट के लिए, प्रोग्राम ऑब्जेक्ट पर कब्जा या रिलीज़ होने वाले स्टोरेज से पहले स्पष्ट रूप से विनाशक को कॉल करने के लिए आवश्यक नहीं है; हालांकि, अगर कोई विनाशक को स्पष्ट कॉल नहीं है या यदि एक डिलीट-एक्सप्रेशन (5.3.5) भंडारण को रिलीज़ करने के लिए उपयोग नहीं किया जाता है, तो विनाशक नहीं कहा जाएगा और कोई भी प्रोग्राम जो साइड इफेक्ट्स पर निर्भर करता है विनाशक द्वारा उत्पादित में अपरिभाषित व्यवहार है।

तो जब ऐसा होता है:

Foo* fooArray = new Foo[numFoos]; //allocate an array of default constructed Foo's 

for(int i = 0; i < numFoos; ++i) 
{ 
    new(fooArray+ i) Foo(initialiser); //use placement new to initialise 
} 

नियुक्ति नई आपरेशन Foo कि वहाँ था के जीवनकाल खत्म हो जाएगा, और यह जगह है में एक नया बनाएँ। कई परिस्थितियों में यह बुरा हो सकता है, लेकिन जिस तरह से आपका विनाशक काम करता है, यह ठीक होगा।

किसी मौजूदा ऑब्जेक्ट पर नया कॉलिंग प्लेसमेंट अपरिभाषित व्यवहार हो सकता है, लेकिन यह विशिष्ट ऑब्जेक्ट पर निर्भर करता है।

यह अपरिभाषित व्यवहार का उत्पादन नहीं करता है, क्योंकि आप विनाशक द्वारा उत्पादित "साइड इफेक्ट्स" के आधार पर नहीं हैं।

केवल "पक्ष प्रभाव" अपनी वस्तु का नाशक में delete को निहित int सूचक है, लेकिन इस मामले कि वस्तु जब नियुक्ति new कहा जाता है एक हटाने-योग्य राज्य में कभी नहीं है में।

यदि यह संभव हो गया था निहित int सूचक nullptr के अलावा कुछ के बराबर होना चाहिए और संभवतः, हटाने तो बुला नियुक्ति new मौजूदा वस्तु पर अपरिभाषित व्यवहार आह्वान होगा की आवश्यकता हो सकती है।

+3

सटीक होने पर, मैं इस पुन: उपयोग को एक भयानक विचार के रूप में मानता हूं। –

+1

हमारे> 30 साल पुराने कोडबेस में बहुत सी समान संरचनाएं हैं और वे सी से सी ++ –

+1

@MooingDuck सहमत हुए अधूरा संक्रमण के सभी परिणाम हैं। धन्यवाद, मैंने यह स्पष्ट कर दिया कि यह एक बुरा विचार है लेकिन इस मामले में यह अच्छी तरह परिभाषित है। –

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