これは初心者の質問だと思いますが、C ++を学ぼうとしているので、この「コールバック」という表現に頻繁に困惑しています。私はそれをグーグルで検索してウィキペディアをチェックしましたが、良い説明が見つかりませんでした。私はいくつかのJavaとC#に精通していますが、それがどれほどありそうもないように聞こえるかもしれませんが、コールバックが何を意味するのかを本当に理解したことはありません。
誰かがこの用語を単純な素人に説明する方法を知っているなら、私は本当に感謝するでしょう。
これは初心者の質問だと思いますが、C ++を学ぼうとしているので、この「コールバック」という表現に頻繁に困惑しています。私はそれをグーグルで検索してウィキペディアをチェックしましたが、良い説明が見つかりませんでした。私はいくつかのJavaとC#に精通していますが、それがどれほどありそうもないように聞こえるかもしれませんが、コールバックが何を意味するのかを本当に理解したことはありません。
誰かがこの用語を単純な素人に説明する方法を知っているなら、私は本当に感謝するでしょう。
私はいくつかのJavaとC#に精通しています
コールバックは、これらの言語でのイベントまたはデリゲートです。つまり、そのコンテキストで他の誰かのコードによってコードを実行させる方法です。したがって、「コールバック」という用語は次のとおりです。
正規の例は、ユーザー定義の比較関数(コールバック)を使用したソートルーチンです。次のような並べ替えルーチンが与えられます。
void Sort(void* values, int length, int valueSize,
int (*compare)(const void*, const void*)
{
for (int i = 0; i < length; i = i + 2) {
// call the callback to determine order
int isHigher = compare(values[i], values[i + 1]);
/* Sort */
}
}
(並べ替えの実行方法の詳細は重要ではありません。並べ替えアルゴリズムで2つの値を比較し、どちらが高いかを判断する必要があるという事実に注目してください。)
これで、いくつかの比較関数を定義できます。
int CompareInts(const void* o, const void* p) {
int* a = (int*) o;
int* b = (int*) p;
if (a == b) return 0;
return (a < b) ? -1 : 1;
}
int ComparePersons(const void* o, const void* p) {
Person* a = (Person*) o;
Person* b = (Person*) p;
if (a == b) return 0;
return (a->Value() < b=>Value()) ? -1 : 1;
}
そして、同じソート関数をそれらで再利用します。
int intValues[10];
Person personValues[10];
Sort(intValues, 10, sizeof(intVaues[0]), CompareInts);
Sort(personValues, 10, sizeof(personVaues[0]), ComparePersons);
メンバー関数を使用している場合は、ポインターを管理する必要があるため、状況は少し複雑になりますthis
が、概念は同じです。ほとんどのものと同様に、最初にCで説明する方が簡単です。;)
コールバックを送信するときは、関数をアドレス指定する方法(たとえば、C ++の関数ポインター)を送信して、送信先のコードが後でその関数を呼び出して、プロセスが完了したときに使用できるようにします。
の違い
start_some_process(some_value, some_function()) # not using a callback
と
start_some_process(some_value, some_function) # using a callback
つまり、最初のインスタンスでは関数の結果を送信し、2番目のインスタンスでは関数自体を送信します。
これはあなたの答えとコード参照を持っています: コールバック
コールバックは、実行中のコードへのフックであり、プロセスの既知のポイントでカスタマイズされた機能を提供できるようにします。これにより、一般化された制御構造が、コード内から呼び出されるコードによって指定されるカスタマイズされた操作を実行できるようになります。したがって、「コールバック」という用語は、コードにコールバックします。
通常、これは、特定の事前定義されたシグネチャを関数ポインターに提供することによって行われます。コールバックを実行する汎用コードは、パラメーターを関数に渡し、特定のタイプの戻り値を期待します。
これは非常に強力なパターンであり、完全に書き直すことなく、コードの再利用と非常に単純なカスタマイズ/拡張を可能にします。
カスタムソート関数は良い例です。ソートアルゴリズムは一般的であり、比較機能はソートされているものに固有です。多くのアルゴリズムでは、比較するオブジェクトであるジェネリック型の2つの引数を取り、比較の結果に応じて+ ve、-ve、またはゼロの値を返すことを期待する関数を提供できます。
次に、comaprison関数を自分で記述し、ソート中に「コールバック」するソートアルゴリズムへの関数ポインターを提供します。
簡単に言うと、コールバックは別のメソッドに渡すコードです。
たとえば、クラスBのメソッドを呼び出すクラスAがありますが、終了時にクラスAから実行するコードが必要です。コードをクラスAの独自の新しいメソッドに配置し、クラスBのメソッドを呼び出すときに、メソッド名を渡します。クラスBのメソッドが処理を完了すると、クラスAに「コールバック」できます。
現在では、コールバックコードを独自のメソッドに配置する必要はありません。使用できる匿名メソッドとラムダ式があります。匿名メソッドを使用する方法を習得するまで、(少なくともC#では)最も混乱が少ないと思います。
幸運を!
PS私は同じでした:私はそれらを本当に理解する前に何年もの間C#をコーディングしていました。
C++でboost::functionをチェックしてください
この回答を別の質問に投稿しましたが、ここでも同じように当てはまるようです。
C ++でコールバックを(おおよそ)最も柔軟なものから最も柔軟でないものまで実装する方法は次のとおりです。
いくつかのシグナルとスロットの実装がここにリストされています(特にBoost.Signal)。これらは、複数のオブジェクトが通知の受信に関心があるオブザーバーデザインパターンを実装するのに役立ちます。
コールバックを登録できboost::function
ます。boost::function
呼び出し可能なエンティティ(フリー関数、静的関数、メンバー関数、または関数オブジェクト)のラッパーです。メンバー関数をラップするにはboost::bind
、この例に示すようにを使用します。使用例:
#include <iostream>
#include <boost/function.hpp>
#include <boost/bind.hpp>
typedef boost::function<void (void)> MouseCallback;
class Mouse
{
public:
void registerCallback(MouseCallback callback) {callback_ = callback;}
void notifyClicked() {if (callback_) callback_();}
private:
MouseCallback callback_;
};
class Foo
{
public:
void mouseClicked() {std::cout << "Mouse clicked!";}
};
int main()
{
Mouse mouse;
Foo foo;
mouse.registerCallback(boost::bind(&Foo::mouseClicked, &foo));
mouse.notifyClicked();
}
FastDelegateと呼ばれるデリゲート実装があります。これは。よりも高速ですboost::function
。これは、C ++標準ではサポートされていないが、実質的にすべてのコンパイラーでサポートされている「醜いハック」を使用しています。
標準でサポートされているが、すべてのコンパイラでサポートされているわけではない、不可能なほど高速なC++デリゲートもあります。
コールバックインターフェース(抽象クラス)から派生したオブジェクトへのポインターを登録できます。これは、コールバックを行う従来のJavaの方法です。例:
class MouseInputListener
{
public:
virtual void mouseClicked() = 0;
virtual void mouseReleased() = 0;
};
class Mouse
{
public:
Mouse() : listener_(0) {}
void registerListener(MouseInputListener* listener) {listener_ = listener;}
void notifyClicked() {if (listener_) listener_->mouseClicked();}
void notifyReleased() {if (listener_) listener_->mouseReleased();}
private:
MouseInputListener* listener_;
};
class Foo : public MouseInputListener
{
public:
virtual void mouseClicked() {cout << "Mouse clicked!";}
virtual void mouseReleased() {cout << "Mouse released!";}
};
コールバックフリー関数へのポインターと、追加の「コンテキスト」voidポインターを登録します。void*
コールバック関数では、イベントを処理するオブジェクトタイプにをキャストし、適切なメソッドを呼び出します。例えば:
typedef void (*MouseCallback)(void* context); // Callback function pointer type
class Mouse
{
public:
Mouse() : callback_(0), context_(0) {}
void registerCallback(MouseCallback callback, void* context = 0)
{callback_ = callback; context_ = context;}
void notifyClicked() {if (callback_) callback_(context_);}
private:
MouseCallback callback_;
void* context_;
};
class Foo
{
public:
void mouseClicked() {cout << "Mouse clicked!";}
static void callback(void* context)
{static_cast<Foo*>(context)->mouseClicked();}
};
int main()
{
Mouse mouse;
Foo foo;
mouse.registerCallback(&Foo::callback, &foo);
mouse.notifyClicked();
}
私はいくつかのパフォーマンスベンチマークを見つけました:
さまざまなパフォーマンス要件に適したコールバックメカニズムについてのアイデアが得られるはずです。
数字からわかるように、パフォーマンスが問題になる前に、ブースト信号を1秒間に10,000〜100,000回呼び出す必要があります。