2012-06-11 48 views
5

मैं void* संकेत करने के लिए कुछ सदस्य समारोह संकेत बदलने की आवश्यकता हो जाता है (क्योंकि मैं उन्हें लुआ को पुश करने के लिए ढेर की जरूरत है, लेकिन समस्या यह संबंधित लुआ नहीं है)।एक सी ++ सदस्य समारोह सूचक कॉलिंग: इस-सूचक भ्रष्ट

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

#include <iostream> 
using namespace std; 

class test 
{ 
    int a; 

    public: 
     void tellSomething() 
     { 
      cout << "this: " << this << endl; 
      cout << "referencing member variable..." << endl; 
      cout << a << endl; 
     } 
}; 

int main() 
{ 
    union 
    { 
     void *ptr; 
     void (test::*func)(); 
    } conv1, conv2; 

    union 
    { 
     void *ptr; 
     void (*func) (test*); 
    } conv3; 

    test &t = *new test(); 

    cout << "created instance: " << (void*) &t << endl; 

    // assign the member function pointer to the first union 
    conv1.func = &test::tellSomething; 

    // copy the void* pointers 
    conv2.ptr = conv3.ptr = conv1.ptr; 

    // call without conversion 
    void (test::*func1)() = conv1.func; 
    (t.*func1)(); // --> works 

    // call with C style function pointer invocation 
    void (*func3) (test*) = conv3.func; 
    (*func3) (&t); // --> works (although obviously the wrong type of pointer) 

    // call with C++ style member function pointer invocation 
    void (test::*func2)() = conv2.func; 
    (t.*func2)(); // `this' is the wrong pointer; program will crash in the member function 

    return 0; 
} 

उत्पादन है:

created instance: 0x1ff6010 
this: 0x1ff6010 
referencing member variable... 
0 
this: 0x1ff6010 
referencing member variable... 
0 
this: 0x10200600f 
referencing member variable... 
zsh: segmentation fault (core dumped) ./a.out 

इस संकलक (जीसीसी) में एक बग है

यहाँ कोड का टुकड़ा है कि समस्या को दर्शाता है है? मुझे पता है कि void* और (सदस्य) फ़ंक्शन पॉइंटर्स के बीच यह रूपांतरण मानक अनुपालन नहीं है, लेकिन अजीब चीज यह है कि यह काम करता है, void* को एक सी स्टाइल फ़ंक्शन पॉइंटर में परिवर्तित करते समय।

+3

क्या आपने अपने memfunptr के आकार() को चेक किया है? मेरे कार्यान्वयन में यह आकार से अधिक है (शून्य *) – PlasmaHH

उत्तर

5

अपने कोड में इन दो पंक्तियों जोड़ें और जवाब स्पष्ट हो जाएगा:

cout << "sizeof(void*)=" << sizeof(conv1.ptr) << endl; 
cout << "sizeof(test::*)=" << sizeof(conv1.func) << endl; 

वजह साफ है। पर विचार करें:

class Base1 
{ 
public: 
int x; 
void Foo(); 
Base1(); 
}; 

class Base2 
{ 
public: 
float j; 
void Bar(); 
Base2(); 
}; 

class Derived : public Base1, public Base2 
{ 
Derived(); 
}; 

जब आप एक Derived पर Foo फोन, this सूचक Base1::x को इंगित करना चाहिए। लेकिन जब आप Bar पर Derived पर कॉल करते हैं, तो this पॉइंटर Base2::j पर इंगित करना चाहिए! इसलिए किसी सदस्य फ़ंक्शन के लिए पॉइंटर में this पॉइंटर के रूप में अपेक्षा की जाने वाली सही प्रकार की कक्षा के उदाहरण को इंगित करने के लिए this पॉइंटर को सही करने के लिए फ़ंक्शन के पते और "समायोजक" दोनों को शामिल करना होगा।

आप समायोजक खो रहे हैं, जिससे this पॉइंटर को यादृच्छिक रूप से समायोजित किया जा सकता है।

+0

ठीक है, बहुत बहुत धन्यवाद, जिसने इसे स्पष्ट किया। मैं पॉइंटर्स को अब एक और 'यूनियन {स्ट्रक्चर {शून्य * पी 1, * पी 2 का उपयोग करके परिवर्तित करता हूं; } पीआरटी; शून्य (परीक्षण :: * func)(); } '। क्या यह किसी भी मामले में काम करेगा? या पहले सदस्य फ़ंक्शन पॉइंटर को आकार देने के लिए बेहतर होगा और फिर तय करें कि कितने 'शून्य *' का उपयोग करना है? –

+0

मुझे आपका पहला दृष्टिकोण बेहतर पसंद है। –

+1

@AlfredKrohmer: आपको शायद पॉइंटर-टू-सदस्य फ़ंक्शन आवंटित करना चाहिए, इसके लिए एक पॉइंटर पास करना चाहिए, और यह पता लगाना चाहिए कि संसाधन का सर्वोत्तम प्रबंधन कैसे करें। निश्चित रूप से यदि आप लुआ के माध्यम से कुछ 'संरचना' पास कर रहे थे, जिसमें कुछ पूर्णांक और एक पॉइंटर शामिल है, तो आप यह काम करने की कोशिश नहीं करेंगे कि इसकी स्मृति में कितनी 'शून्य *' की आवश्यकता है, और इसे कई 'शून्य के रूप में दोबारा परिभाषित करें * '? खैर, एक पॉइंटर-टू-सदस्य-फ़ंक्शन कुछ पूर्णांक और पॉइंटर है। सावधान रहें कि कुछ कार्यान्वयन में, विभिन्न प्रकार के सदस्य फ़ंक्शन के पॉइंटर्स अलग-अलग आकार होते हैं। –

1

अजीब रूप से पर्याप्त, यहां (वीएस2005 के तहत), पहला और तीसरा कॉल ठीक काम करता है लेकिन दूसरा (conv3 के साथ) इस भ्रष्ट होने में विफल रहता है।

+1

एक उड़ान अनुमान के रूप में, वीएस2005 पॉइंटर-टू-फ़ंक्शन के लिए सीडीईसी कॉलिंग सम्मेलन का उपयोग करता है (इसलिए पैरामीटर स्टैक पर पारित किया जाता है), लेकिन सदस्य फ़ंक्शंस के साथ इस कॉलल कन्वेंशन को सम्मिलित करता है (इसलिए कैली को उम्मीद है कि यह ' ECX)। इसलिए यह कोड के सही पते के साथ आने पर भी 'conv3' काम करने से रोकता है। –

+0

यह वही सटीक व्यवहार है जो मुझे होने की उम्मीद है। –

1

ऐसा लगता है कि आपके कार्यान्वयन पर, पॉइंटर-टू-सदस्य-फ़ंक्शन प्रकार void (test::*)() के उदाहरण के पहले size(void*) बाइट्स इस मामले में, स्मृति में किसी फ़ंक्शन का पता होना चाहिए। एक कार्यान्वयन विस्तार के रूप में, यह फ़ंक्शन कॉल करने योग्य है जैसे कि यह पहले पैरामीटर के रूप में this के साथ एक नि: शुल्क फ़ंक्शन था। यही कारण है कि conv3 काम प्रतीत होता है।

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

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