簡體   English   中英

調用C ++成員函數指針:this-pointer被破壞

[英]Calling a C++ member function pointer: this-pointer gets corrupted

我需要將一些成員函數指針轉換為void*指針(因為我需要將它們推送到Lua堆棧,但問題不是Lua相關)。

我是用union來做的。 但是,當我將成員函數指針轉換為void*並再次返回然后嘗試使用該類的實例調用指針時, this指針會被破壞。 扼殺,這個問題不會發生,如果我將void*指針轉換回C風格的函數指針,並帶有指向類的指針作為它的第一個參數。

這是一段演示問題的代碼:

#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

這是編譯器(GCC)中的錯誤嗎? 我知道void*和(member)函數指針之間的這種轉換不符合標准,但奇怪的是,當將void*轉換為C樣式函數指針時,它可以工作。

將這兩行添加到您的代碼中,答案將是明確的:

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 但是當你在Derived上調用Bar時, this指針必須指向Base2::j 因此,指向成員函數的指針必須包含函數的地址和“調整器”以糾正this指針,以指向函數期望作為this指針的正確類型的實例。

您正在丟失調整器,導致this指針隨機調整。

奇怪的是,在這里(在VS2005下),第一次和第三次調用工作正常,但第二次(使用conv3)失敗,因為這被破壞了。

它看起來好像在你的實現上,指向成員函數類型void (test::*) ()的實例的第一個size(void*)字節恰好發生,在這種情況下,是內存中函數的地址。 作為一個實現細節,該函數可以調用,就像它是一個自由函數一樣, this作為第一個參數。 這就是為什么conv3似乎有效。

但是,當嘗試將第一個sizeof(void*)字節復制到指向成員函數類型的指針的不同實例時,運氣已經用完。 conv2的其余部分中的未初始化的垃圾,一旦被解釋為初始代碼地址之后指向成員函數的其余部分,就會出現問題。 我懷疑那里有一些標志和偏移,記錄有關虛函數和多重和虛繼承的信息。 當該信息不正確時,東西出錯了。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM