3

次のタイプのイベント処理が使用されているのをよく見かけます。

Connect(objectToUse, MyClass::MyMemberFunction);

objectToUse が MyClass タイプであるある種のイベント処理用。私の質問は、これがどのように機能するかです。これをどうやって何かに変換しますかobjectToUse->MyMemberFunction()

MyClass::MyMemberFunction関数ポインターとして使用できるクラスの先頭からのオフセットを指定しますか?

4

4 に答える 4

2

通常、これはstaticメンバー関数 (引数としてポインターを取る) を使用します。この場合、objectToUseがパラメーターとして渡され、MyMemberFunctionを使用objectToUseしてオブジェクトへのポインターを設定し、MyClassそれを使用してメンバー変数を参照し、メンバー関数。

この場合Connect、次のような内容が含まれます。

void Connect(void *objectToUse, void (*f)(void *obj))
{
     ...
     f(objectToUse);  
     ...
}

[ and が、実際に Connect 内ではなく、後で使用するためにどこかに保存される可能性もf十分objectToUseにありますが、その場合でも呼び出しは同じように見えます - この関数がイベントの結果として呼び出された他の関数からだけです。呼ばれることになっています]。

メンバー関数へのポインターを使用することも可能ですが、それは非常に複雑であり、構文と「いつ、どのように正しく使用できるか」の両方に関して、「正しく理解する」ことはまったく簡単ではありません。詳しくはこちらをご覧ください

この場合、次のConnectようになります。

void Connect(MyClass *objectToUse, void (Myclass::*f)())
{
     ...
     objectToUse->*f();
     ...
}

テンプレートが使用されている可能性が非常に高く、Connect クラスで "MyClass" が認識されているかのように、関数ポインターを使用しても意味がありません。仮想関数の方がはるかに適しています。

適切な状況があれば、仮想関数をメンバー関数ポインターとして使用することもできますが、「一緒に遊ぶ」にはコンパイラー/環境が必要です。この件に関する詳細は次のとおりです [これについては、個人的な経験はまったくありません: 仮想メンバー関数へのポインター。それはどのように機能しますか?

Vlad はまた、関数をラップするオブジェクトである Functors を指摘し、特定の動作を持つオブジェクトを「関数オブジェクト」として渡すことを可能にします。通常、これには定義済みのメンバー関数、またはoperatorXXコードにコールバックする必要がある関数の処理の一部として呼び出される が含まれます。

C++11 では、コード内でオンザフライで宣言された名前のない関数である「Lambda 関数」を使用できます。これは私がまったく使用したことがないものなので、これ以上コメントすることはできません - 私はそれについて読んだことがありますが、私の(趣味の)プログラミングでそれを使用する必要はありませんでした - 私の仕事の人生のほとんどは私は C++ で 5 年間働いていましたが、C++ ではなく C を使用していました。

于 2013-03-01T00:35:39.917 に答える
2

Mats の回答に加えて、この種の非静的メンバー関数を使用する方法の簡単な例を示します。メンバー関数へのポインターに慣れていない場合は、最初にFAQを確認してください。

次に、この (かなり単純化された) 例を考えてみましょう。

class MyClass
{
public:
    int Mult(int x)
    {
        return (x * x);
    }

    int Add(int x)
    {
        return (x + x);
    }
};

int Invoke(MyClass *obj, int (MyClass::*f)(int), int x)
{ // invokes a member function of MyClass that accepts an int and returns an int
  // on the object 'obj' and returns.
    return obj->*f(x);
}

int main(int, char **)
{
    MyClass x; 

    int nine = Invoke(&x, MyClass::Mult, 3);
    int six = Invoke(&x, MyClass::Add, 3);

    std::cout << "nine = " << nine << std::endl;
    std::cout << "six = " << six << std::endl;

    return 0;
}
于 2013-03-01T00:39:53.783 に答える
1

ここで間違っているかもしれませんが、私が理解している限り、

C++ では、同じシグネチャを持つ関数は同等です。

n 個のパラメーターを持つ C++ メンバー関数は、実際には n+1 個のパラメーターを持つ通常の関数です。つまり、void MyClass::Method( int i )有効void (some type)function( MyClass *ptr, int i)です。

したがって、Connect が舞台裏で機能する方法は、メンバー メソッドのシグネチャを通常の関数シグネチャにキャストすることだと思います。実際に接続を機能させるには、インスタンスへのポインタも必要です。objectToUse

つまり、基本的には、関数へのポインターを使用し、提供されたパラメーターとオブジェクトのインスタンスへのポインターである追加のパラメーターを使用して呼び出すことができるようになるまで、それらをより一般的な型にキャストします。

メソッドが静的な場合、インスタンスへのポインターは意味がなく、単純な型変換になります。非静的メソッドに関連する複雑さをまだ理解していません.boost ::bindの内部を見ることは、おそらくそれを理解するためにあなたがしたいことです:)これが静的関数に対してどのように機能するかです.

#include <iostream>
#include <string>

void sayhi( std::string const& str )
{
    std::cout<<"function says hi "<<str<<"\n";
}

struct A
{
    static void sayhi( std::string const& str )
    {
        std::cout<<"A says hi "<<str<<"\n";
    }
};

int main()
{
    typedef void (*funptr)(std::string const&);
    funptr hello = sayhi;
    hello("you"); //function says...
    hello = (&A::sayhi); //This is how Connect would work with a static method
    hello("you"); //A says...
    return 0;
}
于 2013-03-01T00:40:02.697 に答える
0

イベント処理またはコールバックの場合、通常、コールバック関数とユーザーデータ引数の 2 つのパラメーターを取ります。コールバック関数の署名には、パラメーターの 1 つとしてユーザーデータが含まれます。

イベントまたはコールバックを呼び出すコードは、userdata 引数を使用して関数を直接呼び出します。たとえば、次のようなものです。

eventCallbackFunction(userData);

イベント処理またはコールバック関数では、ユーザーデータを使用して好きなことを行うことができます。

関数はオブジェクトなしで直接呼び出すことができる必要があるため、グローバル関数またはクラスの静的メソッド (オブジェクト ポインターを必要としない) のいずれかにすることができます。

静的メソッドには、静的メンバー変数にアクセスし、他の静的メソッドを呼び出すことしかできないという制限があります (これにはthisポインターがないため)。ここで、userData を使用してオブジェクト ポインターを取得できます。

このことを念頭に置いて、次のコード スニペットの例を見てください。

class MyClass
{
...
public:
    static MyStaticMethod(void* userData)
    {
        // You can access only static members here
        MyClass* myObj = (MyClass*)userdata;
        myObj->MyMemberMethod();
    }

    void MyMemberMethod()
    {
        // Access any non-static members here as well
        ...
    }

...
...
};

MyClass myObject;
Connect(myObject, MyClass::MyStaticMethod);

ご覧のとおり、オブジェクト ポインター (userData から取得) を使用してメンバー メソッドへの呼び出しをチェーンする最初に呼び出される静的メソッドを作成できれば、イベント処理の一部としてメンバー変数やメソッドにもアクセスできます。

于 2013-03-01T00:38:56.103 に答える