2008-09-19 18 views
90

मैं कभी-कभी उन प्रोग्रामों को नोटिस करता हूं जो त्रुटि के साथ मेरे कंप्यूटर पर क्रैश करते हैं: "शुद्ध वर्चुअल फ़ंक्शन कॉल"।"शुद्ध वर्चुअल फ़ंक्शन कॉल" क्रैश कहां से आते हैं?

जब कोई वस्तु किसी सार वर्ग के निर्माण नहीं की जा सकती है तो ये प्रोग्राम कैसे संकलित होते हैं?

उत्तर

92

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

class Base 
{ 
public: 
    Base() { doIt(); } // DON'T DO THIS 
    virtual void doIt() = 0; 
}; 

void Base::doIt() 
{ 
    std::cout<<"Is it fine to call pure virtual function from constructor?"; 
} 

class Derived : public Base 
{ 
    void doIt() {} 
}; 

int main(void) 
{ 
    Derived d; // This will cause "pure virtual function call" error 
} 
+2

किसी भी कारण से संकलक इसे सामान्य रूप से पकड़ नहीं सकता था? – Thomas

+0

मुझे कोई तकनीकी कारण नहीं दिख रहा है कि संकलक इसे क्यों नहीं पकड़ सका। –

+1

जीसीसी मुझे केवल एक चेतावनी देता है: test.cpp: कन्स्ट्रक्टर 'बेस :: बेस()': test.cpp: 4: चेतावनी: कन्स्ट्रक्टर से बुलाया गया सार वर्चुअल वर्चुअल वर्चुअल शून्य बेस :: doIt() लेकिन यह लिंक समय पर विफल रहता है। – Thomas

0

मुझे लगता है कि कुछ आंतरिक कारणों के लिए अमूर्त वर्ग के लिए एक vtbl बनाया गया है (इसे किसी प्रकार की रन टाइम प्रकार की जानकारी के लिए आवश्यक हो सकता है) और कुछ गलत हो जाता है और वास्तविक वस्तु इसे प्राप्त करती है। यह एक बग है। अकेले यह कहना चाहिए कि ऐसा कुछ नहीं हो सकता है।

शुद्ध अटकलें

संपादित करें: लगता है जैसे मैं प्रश्न में मामले में गलत हूँ। ओटीओएच आईआईआरसी कुछ भाषाएं कन्स्ट्रक्टर विनाशक से बाहर Vtbl कॉल करने की अनुमति देती हैं।

+0

यह कंपाइलर में एक बग नहीं है, यदि आपका यही मतलब है। – Thomas

+0

आपका संदेह सही है - सी # और जावा इसे अनुमति दें। उन भाषाओं में, निर्माण के तहत बोहेक्ट्स का अंतिम प्रकार होता है। सी ++ में, ऑब्जेक्ट्स निर्माण के दौरान प्रकार बदलते हैं और यही कारण है कि जब आप एक अमूर्त प्रकार के साथ ऑब्जेक्ट्स ले सकते हैं। – MSalters

+0

* सभी * अमूर्त कक्षाएं, और उनसे व्युत्पन्न वास्तविक वस्तुएं, एक vtbl (वर्चुअल फ़ंक्शन तालिका) की आवश्यकता होती है, जो उस पर वर्चुअल फ़ंक्शंस को सूचीबद्ध किया जाना चाहिए। सी ++ में ऑब्जेक्ट वर्चुअल फ़ंक्शन टेबल सहित अपने सदस्यों को बनाने के लिए ज़िम्मेदार है। रचनाकारों को बेस क्लास से व्युत्पन्न करने के लिए बुलाया जाता है, और विनाशकों को व्युत्पन्न से बेस क्लास में बुलाया जाता है, इसलिए एक सार आधार वर्ग में वर्चुअल फ़ंक्शन तालिका अभी तक उपलब्ध नहीं है। – fuzzyTew

7

आमतौर पर जब आप एक dangling सूचक माध्यम से एक आभासी समारोह कॉल (लाइव डेमो here देखें)।

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

-1

यहां होने के लिए यह एक चुस्त तरीका है। मैं यह अनिवार्य रूप से आज मेरे साथ हुआ था।

class A 
{ 
    A *pThis; 
    public: 
    A() 
    : pThis(this) 
    { 
    } 

    void callFoo() 
    { 
    pThis->foo(); // call through the pThis ptr which was initialized in the constructor 
    } 

    virtual void foo() = 0; 
}; 

class B : public A 
{ 
public: 
    virtual void foo() 
    { 
    } 
}; 

B b(); 
b.callFoo(); 
+1

कम से कम इसे मेरे vc2008 पर पुन: उत्पन्न नहीं किया जा सकता है, Vptr ए के vtable में इंगित करता है जब पहली बार ए के निर्माता में प्रारंभ होता है, लेकिन फिर जब बी पूरी तरह से प्रारंभ होता है, तो Vptr को बी के vtable पर इंगित किया जाता है, जो ठीक है –

+0

coudnt इसे बनाम बनाम 2010/12 – makc

+0

* के साथ पुन: पेश करें * 'यह अनिवार्य रूप से मेरे साथ आज होता है' * स्पष्ट रूप से सच नहीं है, क्योंकि बस गलत है: शुद्ध वर्चुअल फ़ंक्शन केवल तभी कॉल किया जाता है जब एक कन्स्ट्रक्टर (या विनाशक) के भीतर 'कॉलफू()' कहा जाता है) , क्योंकि इस समय वस्तु एक चरण में अभी भी (या पहले से) है। [बी बी(); में सिंटैक्स त्रुटि के बिना आपके कोड का एक चल रहा संस्करण] (https://ideone.com/5zi4Kc) है - '- कोष्ठक इसे एक फ़ंक्शन घोषणा करते हैं, आप एक ऑब्जेक्ट चाहते हैं। – Wolf

60

साथ ही निर्माता या शुद्ध आभासी कार्यों के साथ एक वस्तु का नाशक आप भी एक शुद्ध आभासी समारोह कॉल प्राप्त कर सकते हैं से एक आभासी समारोह बुलाने की मानक मामले (MSVC पर कम से कम) यदि आप एक आभासी फोन वस्तु नष्ट हो जाने के बाद समारोह। जाहिर है यह कोशिश करने और करने के लिए एक बहुत ही बुरी चीज है, लेकिन यदि आप अमूर्त वर्गों के साथ इंटरफेस के रूप में काम कर रहे हैं और आप गड़बड़ कर रहे हैं तो यह कुछ ऐसा है जो आप देख सकते हैं। यदि आप संदर्भित गिनती इंटरफेस का उपयोग कर रहे हैं तो संभवतः अधिक संभावना है और आपके पास एक गिनती बग है या यदि आपके पास बहु-थ्रेडेड प्रोग्राम में ऑब्जेक्ट का उपयोग/ऑब्जेक्ट विनाश दौड़ की स्थिति है ... इस प्रकार के purecall के बारे में बात यह है कि यह है सीटीओआर और डाटर में आभासी कॉल के 'सामान्य संदिग्ध' के लिए चेक के रूप में क्या हो रहा है, यह जानने के लिए अक्सर कम आसान होता है।

एमएसवीसी के विभिन्न संस्करणों में, आप इस प्रकार की समस्याओं को डीबग करने में मदद के लिए रनटाइम लाइब्रेरी के शुद्धकॉल हैंडलर को प्रतिस्थापित कर सकते हैं। आप इस हस्ताक्षर के साथ अपना स्वयं का फ़ंक्शन प्रदान करके ऐसा करते हैं:

int __cdecl _purecall(void) 

और रनटाइम लाइब्रेरी को लिंक करने से पहले इसे लिंक करना। यह आपको शुद्ध करता है जब एक शुद्धकॉल का पता चला है तो क्या होता है। एक बार आपके नियंत्रण होने के बाद आप मानक हैंडलर से कुछ और उपयोगी कर सकते हैं। मेरे पास एक हैंडलर है जो कि पर्सकॉल कहां से एक स्टैक ट्रेस प्रदान कर सकता है; अधिक जानकारी के लिए यहां देखें: http://www.lenholgate.com/blog/2006/01/purecall.html

(ध्यान दें कि आप एमएसवीसी के कुछ संस्करणों में अपने हैंडलर को स्थापित करने के लिए _set_purecall_handler() को भी कॉल कर सकते हैं)।

+0

हटाए गए उदाहरण पर _purecall() आमंत्रण प्राप्त करने के बारे में पॉइंटर के लिए धन्यवाद; मुझे इसके बारे में पता नहीं था, लेकिन सिर्फ थोड़ा परीक्षण कोड के साथ इसे साबित कर दिया। WinDbg में पोस्टमॉर्टम डंप को देखते हुए मैंने सोचा कि मैं एक दौड़ से निपट रहा था जहां एक और धागा पूरी तरह से निर्मित होने से पहले व्युत्पन्न वस्तु का उपयोग करने की कोशिश कर रहा था, लेकिन इससे इस मुद्दे पर एक नई रोशनी चमकती है, और यह सबूत साबित होता है। –

+0

एक और चीज जो मैं जोड़ूंगा: '_purecall()' आमंत्रण जो सामान्य रूप से हटाए गए उदाहरण की विधि को कॉल करने पर होता है, _not_ होता है यदि बेस क्लास को '__declspec (novtable) 'ऑप्टिमाइज़ेशन (माइक्रोसॉफ्ट विशिष्ट) के साथ घोषित किया गया है। । इसके साथ, ऑब्जेक्ट हटा दिए जाने के बाद ओवरराइड वर्चुअल विधि को कॉल करना पूरी तरह से संभव है, जो समस्या को तब तक मुखौटा कर सकता है जब तक कि यह आपको किसी अन्य रूप में काट नहीं देता। '_purecall()' जाल आपका दोस्त है! –

+0

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

0

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

template <typename T> 
class Foo { 
public: 
    Foo<T>() {}; 
    ~Foo<T>() {}; 

public: 
    void SomeMethod1() { this->~Foo(); }; /* ERROR */ 
}; 

तो मैं ले जाया अंदर क्या ~ फू() निजी विधि अलग करने के लिए है, तो यह एक आकर्षण की तरह काम किया है।

template <typename T> 
class Foo { 
public: 
    Foo<T>() {}; 
    ~Foo<T>() {}; 

public: 
    void _MethodThatDestructs() {}; 
    void SomeMethod1() { this->_MethodThatDestructs(); }; /* OK */ 
}; 
संबंधित मुद्दे