2010-01-30 33 views
10

संपादित करें: हलशुद्ध आभासी विधि कहा जाता है

जहां मैं एक आधार कार्यकर्ता वर्ग है मैं एक मल्टी-थ्रेडेड परियोजना पर काम कर रहा हूँ अभी, कार्यकर्ता वर्गों है कि यह से विरासत अलग-अलग। रनटाइम पर, वर्कर क्लासेस थ्रेड बन जाते हैं, जो तब आवश्यकतानुसार काम करते हैं।

अब, मेरे पास एक निदेशक है जिसे मैंने लिखा है जो सभी श्रमिकों को पॉइंटर्स की एक सरणी बनाए रखने के लिए माना जाता है, ताकि वह उनसे जानकारी प्राप्त कर सके, साथ ही बाद में उनके भीतर चर संशोधित कर सके।

baseWorkerClass** workerPtrArray; 

तब निदेशक के निर्माता में, मैं गतिशील संकेत की एक सरणी आधार कार्यकर्ता वर्ग के लिए आवंटित:

मैं आधार वर्ग के सूचक के लिए सूचक बनाने के द्वारा ऐसा किया

workerPtrArray = new baseWorkerClass*[numWorkers]; 

प्रत्येक कार्यकर्ता थ्रेड के निर्माता में, कार्यकर्ता निर्देशक में एक फ़ंक्शन को कॉल करता है जिसका अर्थ उस कार्यकर्ता के सूचक को सरणी में संग्रहीत करना है।

यहाँ कैसे निदेशक संकेत संग्रहीत करता है:

Director::manageWorker(baseWorkerClass* worker) 
{ 
    workerPtrArray[worker->getThreadID()] = worker; 
} 

यहाँ एक कार्यकर्ता संस्करण का एक उदाहरण है। प्रत्येक कर्मचारी को मूल कार्यकर्ता वर्ग से विरासत मिलती है, और आधार कार्यकर्ता वर्ग में शुद्ध वर्चुअल फ़ंक्शंस होते हैं जो सभी कार्यकर्ताओं में मौजूद होना चाहिए, साथ ही साथ कुछ चर जो सभी श्रमिकों के बीच साझा किए जाते हैं।

class baseWorkerClass 
{ 
public: 

    baseWorkerClass() 
    { 
    } 

    ~baseWorkerClass() 
    { 
    } 

    virtual int getThreadID() = 0; 
    virtual int getSomeVariable() = 0; 
}; 

के बाद प्रत्येक कार्यकर्ता संस्करण आरंभ किया जाता है, मैं baseWorkerClass वस्तुओं की ओर इशारा की एक सरणी के साथ खत्म करना चाहिए:

class workerVariant : protected baseWorkerClass 
{ 
    public: 

    workerVariant(int id) 
    : id(id) 
    { 
     Director::manageWorker(this); 
    } 

    ~workerVariant() 
    { 
    } 

    int getThreadID() 
    { 
     return id; 
    } 

    int getSomeVariable() 
    { 
     return someVariable; 
    } 

    protected: 

    int id; 
    int someVariable 
}; 

फिर baseWorkerClass कुछ इस तरह लग रहा है।

workerPtrArray[5]->getSomeVariable(); // Get someVariable from worker thread 5 

समस्या है कि इस कोड एक कारण बनता है: यह मैं करने के लिए, उदाहरण के लिए, किसी दिए गए चर का मान एक निश्चित कार्यकर्ता में सरणी सूचकांक के रूप में अपनी आईडी का उपयोग कर, तो तरह मिल में सक्षम होना चाहिए मतलब है एक Windows निष्पादन में दुर्घटना, क्यों, और लिनक्स में, यह कहते हैं की किसी भी विवरण के बिना:
बुलाया

शुद्ध आभासी विधि एक सक्रिय अपवाद
गर्भपात के बिना बुलाया समाप्त

मैं शपथ ले सकता था कि मैंने कुछ समय पर यह काम किया था, इसलिए मैं उलझन में हूं कि मैंने क्या खराब कर दिया है।


वास्तविक असंशोधित कोड समस्या है जो:

कार्यकर्ता संस्करण हैडर: http://pastebin.com/f4bb055c8
कार्यकर्ता संस्करण स्रोत फ़ाइल: http://pastebin.com/f25c9e9e3

बेस कार्यकर्ता वर्ग हैडर: http://pastebin.com/f2effac5
बेस कार्यकर्ता वर्ग स्रोत फ़ाइल: http://pastebin.com/f3506095b

निदेशक शीर्षलेख: http://pastebin.com/f6ab1767a
निदेशक स्रोत फ़ाइल: http://pastebin.com/f5f460aae


संपादित करें: अतिरिक्त जानकारी, manageWorker समारोह में, मैं सूचक से शुद्ध आभासी कार्यों के किसी भी कॉल कर सकते हैं "कार्यकर्ता," और यह सिर्फ ठीक काम करता है। प्रबंधन वर्कर फ़ंक्शन के बाहर, जब मैं पॉइंटर सरणी का उपयोग करने का प्रयास करता हूं, तो यह विफल हो जाता है।

संपादित करें: अब जब मैं इसके बारे में सोचता हूं, थ्रेड का प्रवेश बिंदु ऑपरेटर() है। निदेशक धागे श्रमिकों के सामने बनाया गया है, जिसका अर्थ यह हो सकता है कि अधिभारित कोष्ठक ऑपरेटर शुद्ध वर्चुअल फ़ंक्शंस को कॉल कर रहा है इससे पहले कि उन्हें बाल कक्षाओं द्वारा ओवरराइड किया जा सके। मैं इसमें देख रहा हूँ

उत्तर

12
समस्या

है कि Director::manageWorkerworkerVariant उदाहरणों में से निर्माता में कहा जाता है प्रकट होता है:

Director::manageWorker(baseWorkerClass* worker) { 
    workerPtrArray[worker->getThreadID()] = worker; 
} 

मुमकिन getThreadID() एक शुद्ध आभासी समारोह नहीं है या आप के लिए होता है मिल गया के बारे में नहीं एक संकलक त्रुटि (उम्मीद!) इसे workerVariant में ओवरराइड करना। लेकिन getThreadID() अन्य कार्यों को कॉल कर सकता है जिन्हें आपको ओवरराइड करना चाहिए, लेकिन अमूर्त वर्ग में बुलाया जा रहा है। आपको यह सुनिश्चित करने के लिए getThreadID() की परिभाषा को दोबारा जांचना चाहिए कि आप कुछ भी नहीं कर रहे हैं जो ठीक से शुरू होने से पहले बाल वर्ग पर निर्भर करेगा।

इस प्रकार के मल्टी-स्टेज प्रारंभिकरण को एक अलग विधि में अलग करने या Director और baseWorkerClass को डिज़ाइन करने के लिए एक बेहतर समाधान हो सकता है कि उनके पास प्रारंभिक समय-समय पर परस्पर निर्भरता नहीं है।

+0

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

+13

जब कोई ऑब्जेक्ट निर्माणाधीन होता है (जब कन्स्ट्रक्टर निष्पादित किया जाता है), वर्चुअल फ़ंक्शन तंत्र ऑब्जेक्ट के प्रकार से प्राप्त कक्षा के आभासी कार्यों में कभी भी कॉल नहीं करेगा। ऐसा इसलिए है क्योंकि व्युत्पन्न कक्षाओं के रचनाकारों को अभी तक निष्पादित नहीं किया गया है।इसलिए कन्स्ट्रक्टर से व्युत्पन्न क्लास विधि को कॉल करना काम नहीं करेगा। इनमें से एक तरीका दो चरण का निर्माण है, अधिमानतः एक रैपर ऑब्जेक्ट के पीछे छिपा हुआ है। (विनाश के लिए वही है, बीटीडब्ल्यू।) – sbi

+0

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

2

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

2

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

महत्वपूर्ण जगहों पर आवेषण डालने का प्रयास करें जहां आप यह सुनिश्चित करने के लिए सरणी को संदर्भित कर रहे हैं कि सूचकांक समझ में आता है। अर्थात। 4 श्रमिकों तक सीमित करें और सुनिश्चित करें कि आईडी 4 से कम है।

+0

अब जब आप इसका जिक्र करते हैं, तो मुझे याद है कि विज़ुअल स्टूडियो डीबगर को सरणी तत्वों के बारे में शिकायत करने की शिकायत है जो सीमा से बाहर थे। मुझे नहीं लगता कि यह क्यों हो रहा है, क्योंकि मैंने अपने सूचकांक की जांच की है और दोबारा जांच की है, और वे सभी सीमाओं के भीतर हैं। मैं कुछ मिनटों में अपना वास्तविक कोड पोस्ट कर दूंगा। – jakogut

+1

आप डबल पॉइंटर के बजाय std :: vector का उपयोग करने का प्रयास कर सकते हैं। मुझे हमेशा इस तरह के सरणी आवंटित करने के लिए "नया" वाक्यविन्यास देखना होगा। साथ ही, यह थोड़ा और स्पष्ट होगा और सरणी के प्रबंधन से आपको कम करेगा। –

2

प्रारंभिक कक्षाओं के दौरान केवल आंशिक रूप से निर्मित होते हैं।विशेष रूप से, रचनाकारों को सबसे पूर्वजों के वर्ग से निष्पादित किया जाना चाहिए, ताकि प्रत्येक व्युत्पन्न वर्ग का निर्माता सुरक्षित रूप से अपने मूल सदस्यों तक पहुंच सके।

इसका मतलब है कि आंशिक रूप से निर्मित वर्ग का vtable अपने अंतिम राज्य में नहीं हो सकता है - यदि वर्चुअल विधियों को व्युत्पन्न कक्षाओं को कॉल करने की अनुमति दी गई थी, तो उन विधियों को कक्षा वर्ग निर्माता के समक्ष बुलाया जाएगा।

जिसका मतलब है कि, निर्माण के दौरान, शुद्ध वर्चुअल फ़ंक्शन वास्तव में शुद्ध वर्चुअल हैं। आधुनिक सी ++ कंपाइलर्स इसे पकड़ने में बेहतर हो रहे हैं - लेकिन कई मामलों में अवैध कॉल को इस तरह से "दफन" करना संभव है कि संकलक त्रुटि को नोटिस नहीं करता है।

कहानी का नैतिक: अपने कन्स्ट्रक्टर में कुछ भी न करें जो वर्चुअल फ़ंक्शन का आह्वान करेगा। यह सिर्फ वही नहीं करेगा जो आप उम्मीद करते हैं। यहां तक ​​कि जब यह शुद्ध नहीं है।

1

मुझे आपके किसी भी कोड नमूने में वेरिएंट क्लास का निर्माण नहीं हुआ। क्या आप सुनिश्चित हैं कि आईडी पास की जा रही है कार्यकर्ता सरणी के लिए सीमा के भीतर है? इसके अलावा, आप 'नया' का उपयोग कर वस्तुओं का निर्माण कर रहे हैं, है ना? यदि आपने स्टैक पर ऑब्जेक्ट बनाया है, तो यह खुद को निदेशक के साथ पंजीकृत करेगा, लेकिन कन्स्ट्रक्टर रिटर्न के बाद ऑब्जेक्ट तुरंत नष्ट हो जाएगा, लेकिन निदेशक उस ऑब्जेक्ट को उस पॉइंटर को बनाए रखेगा जो स्टैक पर था।

इसके अलावा, आपके बेसवर्कर क्लास विनाशक वर्कर वर्चुअल डिस्ट्रक्टर के साथ आभासी होना चाहिए ताकि यह सुनिश्चित किया जा सके कि जब आप baseWorkerClass की सरणी हटाते हैं तो उन्हें कॉल किया जाता है।

मेरी टिप्पणी से दूसरे प्रश्न पर, डबल पॉइंटर के बजाय std :: vector का उपयोग करने पर विचार करें। सरणी को बनाए रखने के लिए अपनी आवश्यकता को बनाए रखना और समझना और निकालना आसान है।

ऐसा लगता है कि आपने यहां अमूर्तता की एक अनावश्यक परत जोड़ दी है। मुझे नहीं लगता कि आईडी वास्तव में उपclass इंटरफेस का हिस्सा होना चाहिए। किसी भी वस्तुओं तक पहुँचने संयोग से

class baseWorkerClass 
{ 
public: 

    baseWorkerClass(int id) : 
     id(id) 
    { 
    } 

    virtual ~baseWorkerClass() 
    { 
    } 

    int getThreadID(){ return id; }; 
    virtual int getSomeVariable() = 0; 

protected: 
    int id; 
}; 

class workerVariant : protected baseWorkerClass 
{ 
    public: 

    workerVariant(int id) : 
     baseWorkerClass(id) 
    { 
     Director::manageWorker(this); 
    } 

    virtual ~workerVariant() 
    { 
    } 

    int getSomeVariable() 
    { 
     return someVariable; 
    } 

protected: 
    int someVariable 
}; 
0

रहे हैं नहीं आप के बाद वे विलुप्त कर रहे हैं: मुझे लगता है कुछ इस तरह आप के लिए बेहतर काम कर सकते हैं? क्योंकि विनाश के दौरान vtable पॉइंटर्स धीरे-धीरे "वापस लुढ़का" होते हैं ताकि vtable प्रविष्टियां बेस क्लास के तरीकों को इंगित करें, जिनमें से कुछ सार हैं। ऑब्जेक्ट को हटाने के बाद, स्मृति को छोड़ा जा सकता था क्योंकि यह बेस क्लास के विनाशक के दौरान था।

मेरा सुझाव है कि आप valgrind या MALLOC_CHECK_=2 जैसे मेमोरी डीबगिंग टूल आज़माएं। यूनिक्स पर भी ऐसी घातक त्रुटियों के लिए एक स्टैकट्रैक प्राप्त करना काफी आसान है। बस अपना आवेदन gdb, या TotalView के अंतर्गत चलाएं, और बिंदु पर त्रुटि होती है, यह स्वचालित रूप से रुक जाएगी, और आप ढेर को देख सकते हैं।

0

मैं एक बार यह त्रुटि संदेश मिल गया है, और हालांकि यह प्रश्नकर्ता के सटीक मामले से संबंधित नहीं है, मैं उम्मीद में इस ऐड कि यह दूसरों के लिए उपयोगी हो सकता है:

मैं एक साफ निर्माण करके समस्या का समाधान हो ।

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