2010-03-25 10 views
25

क्लास कन्स्ट्रक्टर के दौरान C++ में, मैंने this पॉइंटर के साथ एक नया थ्रेड शुरू किया जो पैरामीटर के रूप में बड़े पैमाने पर उपयोग किया जाएगा (कहें, सदस्य कार्य को कॉल करना)। क्या यह एक बुरी चीज है? क्यों और क्या परिणाम हैं?सी ++ कन्स्ट्रक्टरों में इस सूचक का उपयोग

मेरा धागा प्रारंभ प्रक्रिया कन्स्ट्रक्टर के अंत में है।

उत्तर

18

परिणाम यह है कि धागा शुरू हो सकता है और कोड अभी तक पूरी तरह से शुरू की गई वस्तु को निष्पादित करना शुरू कर देगा। जो खुद में काफी खराब है।

आप विचार कर रहे हैं कि फिर से लगता है कि 'ठीक है, यह निर्माता में अंतिम वाक्य हो जाएगा, यह सिर्फ के बारे में के रूप में निर्माण के रूप में यह हो जाता है ... हो जाएगा': आप उस वर्ग से निकाले जाते हैं हो सकता है, और व्युत्पन्न वस्तु बनाया नहीं जाएगा।

संकलक चारों ओर अपने कोड के साथ खेलते हैं और तय करते हैं कि यह निर्देश को पुन: व्यवस्थित करेंगे कर सकते हैं और यह वास्तव में कोड के किसी अन्य भाग निष्पादित करने से पहले this सूचक पारित हो सकता है ... बहु सूत्रण मुश्किल

+1

@ गिल्बर्टक: यदि बी ए से निकला है, भले ही आप ए के कन्स्ट्रक्टर के अंत में धागा लॉन्च करते हैं, बी (vtable, सदस्य चर, कन्स्ट्रक्टर कोड) से सबकुछ प्रारंभ/निष्पादित नहीं किया जाएगा। क्या यह एक बुरा संयोग बग हो सकता है? पहली नज़र में, मैं हाँ कहूंगा, क्योंकि वस्तु निर्माण धागे में बदल जाएगी, जबकि इसे नव निर्मित धागे में इस्तेमाल किया जा सकता है। जो यह वैसे भी ऐसा नहीं करना चाहिए - – paercebal

1

क्या है पर निर्भर करता है धागा शुरू करने के बाद आप करते हैं। यदि आप के बाद प्रारंभिक कार्य निष्पादित करते हैं तो थ्रेड शुरू हो गया है, तो यह उस डेटा का उपयोग कर सकता है जो ठीक से प्रारंभ नहीं हुआ है।

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

लेकिन मुझे लगता है कि डिजाइन में सबसे बड़ी खामियां यह है कि, कम से कम मेरे लिए "निर्माण" से अधिक एक कन्स्ट्रक्टर काफी भ्रमित लगता है।

0

यह ठीक है, जब तक आप उस सूचक का तुरंत उपयोग शुरू कर सकते हैं। यदि आपको नए थ्रेड पॉइंटर का उपयोग करने से पहले प्रारंभिकता को पूरा करने के लिए शेष कन्स्ट्रक्टर की आवश्यकता होती है, तो आपको कुछ सिंक्रनाइज़ेशन करने की आवश्यकता है।

4

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

यह विशेष रूप से समस्याग्रस्त है यदि आपकी कक्षा एक बेस क्लास है, क्योंकि व्युत्पन्न क्लास कन्स्ट्रक्टर आपके कन्स्ट्रक्टर के बाहर आने तक भी शुरू नहीं होगा, और व्युत्पन्न क्लास विनाशक आपके शुरू होने से पहले पूरा हो जाएगा। साथ ही, वर्चुअल फ़ंक्शन कॉल ऐसा नहीं करते हैं जो आप व्युत्पन्न कक्षाओं के निर्माण से पहले सोच सकते हैं और जब वे नष्ट हो जाते हैं: वर्चुअल कॉल ऑब्जेक्ट्स का "अनदेखा" होता है जिसका ऑब्जेक्ट का हिस्सा मौजूद नहीं होता है।

उदाहरण:

struct BaseThread { 
    MyThread() { 
     pthread_create(thread, attr, pthread_fn, static_cast<void*>(this)); 
    } 
    virtual ~MyThread() { 
     maybe stop thread somehow, reap it; 
    } 
    virtual void id() { std::cout << "base\n"; } 
}; 

struct DerivedThread : BaseThread { 
    virtual void id() { std::cout << "derived\n"; } 
}; 

void* thread_fn(void* input) { 
    (static_cast<BaseThread*>(input))->id(); 
    return 0; 
} 

अब अगर आप एक DerivedThread बनाते हैं तो वह धागा है कि यह और नए धागा निर्माण करती है जो सबसे अच्छा एक दौड़ का निर्धारण करने के id() का कौन सा संस्करण कहा जाता हो जाता है,। ऐसा हो सकता है कि कुछ और खराब हो सकता है, आपको अपने थ्रेडिंग एपीआई और कंपाइलर पर काफी बारीकी से देखना होगा।

इस बारे में चिंता करने का सामान्य तरीका सिर्फ आपकी थ्रेड क्लास को start() फ़ंक्शन देना है, जिसे उपयोगकर्ता इसे बनाने के बाद कॉल करता है।

1

यह संभावित रूप से खतरनाक हो सकता है।

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

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

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

1

मैं कहूंगा कि, एक सामान्य नियम के रूप में, आपको ऐसा करने से बचना चाहिए। लेकिन आप निश्चित रूप से कई परिस्थितियों में इससे दूर हो सकते हैं। मुझे लगता है कि मूल रूप से दो चीजें हैं जो गलत हो सकती हैं:

  1. नया धागा कन्स्ट्रक्टर इसे प्रारंभ करने से पहले ऑब्जेक्ट तक पहुंचने का प्रयास कर सकता है। आप थ्रेड शुरू करने से पहले यह सुनिश्चित कर सकते हैं कि सभी प्रारंभिकरण पूर्ण हो जाएं। लेकिन अगर कोई आपकी कक्षा से विरासत में आता है तो क्या होगा? आपका कन्स्ट्रक्टर क्या करेगा पर आपका कोई नियंत्रण नहीं है।
  2. यदि आपका धागा शुरू करने में विफल रहता है तो क्या होता है? एक निर्माता में त्रुटियों को संभालने के लिए वास्तव में एक साफ तरीका नहीं है। आप एक अपवाद फेंक सकते हैं, लेकिन यह खतरनाक है क्योंकि इसका मतलब है कि आपके ऑब्जेक्ट के विनाशक को बुलाया नहीं जाएगा। यदि आप अपवाद फेंकने का चुनाव नहीं करते हैं, तो आप यह जांचने के लिए कि क्या चीजों को ठीक से शुरू किया गया था, आप अपने विभिन्न तरीकों से लेखन कोड फंस गए हैं।

आम तौर पर, यदि आपके पास जटिल, त्रुटि-प्रवण प्रारंभ करने के लिए प्रारंभिक है, तो इसे कन्स्ट्रक्टर की बजाय विधि में करना सर्वोत्तम है।

+0

dtor निर्माण विफलता पर बुलाया होने इतना ही खतरनाक है, तो अपनी कक्षा सीधे कच्चे संसाधनों संभालती है। चूंकि अपवाद ही निर्माण विफलताओं की रिपोर्ट करने का एकमात्र तरीका है, इसलिए निश्चित रूप से उन्हें लागू करने के लिए स्लॉप्लीली लागू कक्षाओं को अनुमति देने के लिए प्रतिबंधित नहीं किया जाना चाहिए। हालांकि, मैं जो भी कहता हूं, उससे सहमत हूं। – sbi

+0

मैंने यह नहीं कहा कि इसे प्रतिबंधित किया जाना चाहिए, केवल इतना ही खतरनाक है। यह सुनिश्चित करने के लिए एक डिग्री की देखभाल होती है कि अपवाद के मुकाबले सब कुछ ठीक से साफ हो गया है। यह विशेष रूप से कठिन हो सकता है जब आपकी कक्षा उस कोड पर निर्भर करती है जिसे आपने नहीं लिखा था और नियंत्रण नहीं किया था। –

1

असल में, आपको क्या चाहिए दो चरण निर्माण है: आप अपने धागा केवल के बाद वस्तु पूरी तरह से निर्माण किया है शुरू करना चाहते हैं। John Dibling answered कल एक समान (डुप्लिकेट) प्रश्न नहीं था, जो दो चरण के निर्माण पर व्यापक रूप से चर्चा करता था। आप इसे देखना चाहते हैं।

नोट, हालांकि, यह अभी भी समस्या को छोड़ देता है कि एक व्युत्पन्न वर्ग 'कन्स्ट्रक्टर के पहले धागा शुरू किया जा सकता है।

class Thread { 
    public: 
    Thread(); 
    virtual ~Thread(); 
    void start(); 
    // ... 
}; 

class MyThread : public Thread { 
    public: 
    MyThread() : Thread() {} 
    // ... 
}; 

void f() 
{ 
    MyThread thrd; 
    thrd.start(); 
    // ... 
} 
0

कुछ लोगों को लगता है कि आप this उपयोग नहीं करना चाहिए: (। व्युत्पन्न वर्गों 'कंस्ट्रक्टर्स उनके आधार वर्ग के उन लोगों के बाद कहा जाता है)

तो अंत में सबसे सुरक्षित बात शायद मैन्युअल धागा शुरू करने के लिए है एक कन्स्ट्रक्टर में पॉइंटर क्योंकि ऑब्जेक्ट अभी तक पूरी तरह से गठित नहीं हुआ है। हालांकि यदि आप सावधान हैं तो आप इसे कन्स्ट्रक्टर ({body} में और यहां तक ​​कि प्रारंभिक सूची में भी) में उपयोग कर सकते हैं।

यहां कुछ ऐसा है जो हमेशा काम करता है: {body} एक कन्स्ट्रक्टर (या कन्स्ट्रक्टर से बुलाया गया फ़ंक्शन) बेस क्लास में घोषित डेटा सदस्यों और/या कन्स्ट्रक्टर की अपनी कक्षा में घोषित डेटा सदस्यों को विश्वसनीय रूप से एक्सेस कर सकता है। ऐसा इसलिए है क्योंकि उन सभी डेटा सदस्यों को कन्स्ट्रक्टर के {बॉडी} निष्पादन शुरू होने के समय पूरी तरह से निर्मित होने की गारंटी है।

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

यहाँ कुछ है कि कभी कभी काम करता है। अच्छी खबर यह है कि आप यह निर्धारित कर सकते हैं कि अन्य डेटा सदस्य ने आपके द्वारा उपयोग किए जा रहे विशेष कंपाइलर से स्वतंत्र कुछ सरल भाषा नियमों का उपयोग करके प्रारंभ किया है (या नहीं)। बुरी खबर यह उन भाषा नियमों को जानना है कि आप (जैसे, आधार वर्ग उप वस्तुओं पहले प्रारंभ किया है (क्रम को देखने यदि आप कई और/या आभासी विरासत है!), तो कक्षा में परिभाषित डेटा सदस्यों में प्रारंभ कर रहे हैं जिस क्रम में वे कक्षा घोषणा में शामिल होते हैं)। यदि आप इन नियमों को नहीं जानते हैं, तो किसी भी अन्य डेटा के सदस्य के प्रारंभकर्ता करने के लिए (या नहीं, आप स्पष्ट रूप से thiskeyword का उपयोग की परवाह किए बिना) इस वस्तु से कोई डेटा सदस्य पारित नहीं है! और यदि आप नियमों को जानते हैं, तो कृपया सावधान रहें।

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