0

私はホットな2つのクラスを持っています。そのうちの 1 つが画像で、もう 1 つがページであるとします。Image オブジェクトは OnPointerPressed イベントを受け取ります。それが発生した場合、Page calss から関数を呼び出したいと思います。問題は、ユニオン ハックを使用してメンバー関数のアドレスをイメージに渡すまで、この関数が定義されないことです。このメソッドが Image クラスから呼び出されると正常に動作しますが、メソッド内では Page オブジェクトとそのメンバーが見えません。

MainPage::MainPage(void)
{
    //.......
    image1->OnPointerPressedHandler->add(&MainPage::imageMousePressed);
}

void MainPage::imageMousePressed(STObject* sender, STPointerEventArg * args)
{
    void * dsda = this;  //the address of this is something wierd
    pres = true;
}

誰か少し手伝ってくれませんか?

template<class T>
void add(T const & x)
{
    union hlp_t{
            void * v_ptr;
            T t;
    };
    hlp_t a;         
    a.t = x;
    functions->addLast(a.v_ptr);
}

template<class ARG>
void trigger(SENDERTYPE sender, ARG arg)
{
    STLinkedListIterator<void *>* it = functions->createIteator();
    if(it != nullptr)
    {
        do
        {
            void (*callback)(SENDERTYPE,ARG) =static_cast<void (*)(SENDERTYPE,ARG)>(it->getData());
            callback(sender,arg);
            /*
            void * myVoid = it->getData();
            __asm
            {
                push [arg];
                push [sender];
                call [myVoid];
            }
            */
            it->next();
        }while(!it->EOL());
    }
}
4

2 に答える 2

1

コードのアセンブリ言語バージョンと非アセンブリ言語バージョンの両方で、メンバー関数を間違って呼び出しています。フリー関数 (および静的メンバー関数) の呼び出しには適用されないメンバー関数の特定の要件があります。これは、コードの両方のバージョンが行っていることとまったく同じです。非静的メンバー関数を呼び出す場合、thisポインターは最初のパラメーターとして暗黙的に渡されます。これは、ポインター値をスタックにプッシュするか、レジスターに配置するか、またはその両方によって行うことができます。実際には、コンパイラーが呼び出しコードを生成する方法に依存します。

コードの現在のバージョンは、メンバー関数をフリー関数であるかのように呼び出そうとしますが、スタック上のオブジェクト ポインターを渡します。これは非常にthis悪い考えであり、 の値が正しくない理由です。またARG、オブジェクトが参照またはポインターではなく値によって渡される場合、期待どおりに機能しない可能性があります。これは、オブジェクト全体をスタックにコピーする必要があるためです。これを自分でやろうとしないでください。代わりに、ポインターによってメンバー関数を呼び出せるようにすることを特に意図した、言語によって提供される機能を使用する必要があります。

メンバー関数へのポインターの構文は次のとおりですreturntype (ClassType::*)(parameters)。正しい構文を使用することで、現在行っているようにあいまいなバグを見つけようとする代わりに、間違った構文を使用したときにコンパイラに通知してもらうことができます。メンバー関数を呼び出すときにこれを使用するだけでなく、ポインターをリストに格納するために使用する必要があります。VC++ では、関数ポインターを void ポインターに変換できますが、これは言語に対する Microsoft の拡張機能であり、標準の C++ではありません。void可能な限り避けることをお勧めします。

行う必要がある1 つの変更は、メンバー関数の呼び出し方法です。これは、フリー関数へのポインタを使用する場合とは異なります。次は正しい構文を示しています。

(objectpointer->*functionpointer)(arguments);

演算子の優先順位により追加の括弧のセットが必要であり、これ->*は「メンバーへのポインター」演算子です。以下のコードは、メンバー関数を正しく呼び出すためにコードに加える必要がある変更を示しています。

template<class ARG>
void trigger(STObject* sender, ARG arg)
{
    STLinkedListIterator<void *>* it = functions->createIteator();
    if(it != nullptr)
    {
        do
        {
            // cast to a pointer to member function so the compiler knows
            // the correct type of the function call.
            void (STObject::*callback)(ARG)
                = static_cast<void (STObject::*)(ARG)>(it->getData());

            // call it using the correct syntax and let the compiler
            // handle all the gory details.
            (sender->*callback)(arg);

            it->next();
        }while(!it->EOL());
    }
}

テンプレート パラメーターを追加してターゲット オブジェクトの型を指定することで、これをより汎用的にすることができます。

template<class SENDER, class ARG>
void trigger(SENDER* sender, ARG arg)
{
    STLinkedListIterator<void *>* it = functions->createIteator();
    if(it != nullptr)
    {
        do
        {
            void (SENDER::*callback)(ARG)
                = static_cast<void (SENDER::*)(ARG)>(it->getData());

            (sender->*callback)(arg);

            it->next();
        }while(!it->EOL());
    }
}

.

補足として、関数には未定義の動作add()が含まれています。C++ 言語標準の $9.5/1 から

共用体では、いつでも最大 1 つのデータ メンバーをアクティブにできます。つまり、最大で 1 つのデータ メンバーの値をいつでも共用体に格納できます。

add()の値を設定し、 の値を読み取りtますv_ptr

于 2013-06-02T16:32:52.720 に答える
0

これを機能させるには、「this」ポインターも渡す必要があります。そうしないと、クラスの別のインスタンスがインスタンス化されます。

次のように imageMousePressed を変更する必要があります。

// add in a void* argument    
void MainPage::imageMousePressed(STObject* sender, STPointerEventArg * args, void* mainPageObject)
    {
        MainPage* dsda = (MainPage*) mainPageObject; // now, you should have a pointer to the valid object
        pres = true;
    }

これは、これに対応するために OnPointerPressedHandler の add 関数も変更する必要があることを意味します。

    image1->OnPointerPressedHandler->add(&MainPage::imageMousePressed, this);

そして、内部的には、「this」値を渡す指定された関数の呼び出しを渡す必要があります。これが機能しない理由は、関数が静的関数のようにインスタンス化され、新しいオブジェクトの一部になる場合、古いオブジェクトの状態が保持されないためです。

PS: これに答える人々により良いアイデアを提供するために、より多くのコードを貼り付ける必要があります

于 2013-06-02T07:32:49.250 に答える