C++ソフトC#シリーズ-std::function
8516 ワード
C#を使ったことがある人は、依頼や事件をよく知っています.
C#を使ったことがなければ、ここで簡単に依頼を説明しますが、使ったことがあれば、次はskipでいいです.
依頼はメソッド宣言であり,C言語では関数ポインタでアドレスを表すCALLメソッドが可能であり,依頼はC#でも少なくないことが分かっている.
しかし、依頼にはいくつかの違いがあります.主なところは、C++の中で、
関数ポインタは「オブジェクト向け」ではありません.たとえば、クラスCTestがあり、クラスにメンバーメソッドfooがあります.この場合、関数ポインタでfooを呼び出すには、fooがクラスCTestのメンバーであるため、CTestのインスタンスthisポインタが必要です.
たとえば、
呼び出しはどうしますか?次のようにします.
もしCTestがHEAPになかったら、stackにあったのでしょうか?
次のようにします.
これらの奇抜な文法は,->*,.*この不思議な演算子は本当に卵が痛い.
スレッドを起動するとき、static関数からこのようなsbの構文を通じて、クラスthisをパラメータとして渡し、クラスのメンバー関数を走るのが一般的です.
でもC#では、
委任はオブジェクト向けで、クラスインスタンスのメンバー関数ポインタを委任で表すことができます.
例:
123
1234
this CNewTest.
つまり、C#では、メソッドが依頼声明に適応すれば、callを呼び出すことができ、C#は自動的にthisポインタを隠してくれます.
したがって,C#にイベントが誕生し,1つのプロバイダオブジェクトが1つの依頼を定義し,依頼をイベントとして宣言すると,他のリスナーオブジェクトがこのイベントを自分のメンバー関数に関連付けることができ,プロバイダが必要に応じてこの依頼を呼び出すのを待つと,メンバー関数が呼び出され,イベントがトリガーされる.
なぜ事件と宣言し、依頼を直接暴露すればいいのではないでしょうか.
これは論理的な問題で、直接依頼を暴露すれば、購読者は自発的に依頼をトリガーすることができ、購読者は自発的に、また「購読」することができます.
イベントを使用する場合、サブスクライバはプロバイダが依頼をトリガーするのを待つしかなく、この場合、サブスクライバは「受動的」になるしかありません.
この概念は、C++で理解されています.つまり、
クラスAにはpublicのptr、ptr_がありますthis、このptrはメンバー関数ポインタを保存します.ptr_thisはptrのクラスインスタンスを保存します.
類Bはこのptrを自分のptrに変えた.ptr_thisをthisに変更し、このメンバー関数ポインタの宣言に適応します.
クラスAはいくつかの仕事が終わった後、ptrをチェックします!=nullptr,callというptrはクラスBのメンバ関数に呼び出される.
しかしC++はこれらの天然の不足があって、ptrはnullptrではありませんてむしろできて、しかしクラスBはとっくにdeleteされたかもしれなくて、しかしあなたのクラスAのptrに保存して依然としてnullptrではありませんて、あなたのcallは直接掛けて、もちろんC 11のshared_を使いますptr、weak_ptrというものは、解決策でもありますが、総じて言えば、不快で、これはどんなに面倒ですか.
つまり、C++では、メモリを手動で管理する輪から抜け出すことはできません.
C#のGC自動管理はこれらの問題を完全にクリアします.
もう1つは、C#の中で、依頼はマルチキャストの実現です.例えば:
123
123
this CNewTest.
简単に言えば、cbFというものの中に、汎用的なリストがあります...Callの時は、ランニングメーターが1つずつcallが来ていて、とても便利でした.
はっきり言って、依頼は1つの【方法のインタフェース】で、方法が正しければいいので、安全で気持ちがいいです.
そこで依頼+イベントというマルチキャストを実現することで,観察者モードをうまく作ることができる.
そんなに多くの弊害が出てきて、C 11委員会もこれらの問題を発見して、std::function、これは、簡単に言えば、それは依頼の機能を実現するのにあまり役に立たない(事件を実現するのではありません).
やはり上のC#に書いてあるConsoleApplication 1に戻って、std::functionでもう一度やり直します.
(コンパイル環境はVS 2013、Windows SDK.)
std::functionを使用する前に、includeを含める必要があります.
関数を用意します.
直接的な関数、クラスメンバー関数です.
まず、StaticCallbackを呼び出します.
このように書くのは少し多いですね.うん、私たちはC++で、typedefがあります.
typedefは1行のコードが多くて、しかも直観的ではありませんて、恐れないで、私达はautoがあります.
(
std::functionはlamdbaという匿名関数と組み合わせて使用することもできます.逆天レベルの効果があります.ここではあまり詳しくありません.私は後でlamdbaの文章を書くかもしれません.)
このtmdの依頼がC#に似ているような気がしますか?しかし、C#の依頼はnewで、私たちのC++のは直接stackの上で開くことができて、もちろん私たちのnew、同じくできます.
次に、呼び出しメンバー関数ポインタをテストします.
123
1234
this CNewTest.
std::bindを使用してstd::functionを生成し、このfunctionを呼び出すと過去のメンバー関数を呼び出すことができます.
今私たちのCNewTestはHeapにいます.もし私たちがstackにいたら?きっと思いついたでしょう.
多くの人がおかしいかもしれませんが、後ろのstd::placeholders::1どういう意味ですか.
実はbindの上のこの関数の方法を表すのは1つのパラメータがあって、このパラメータは汎型で、もし私たちがそれに汎型のパラメータを生成させないならば、直接1つの絶対値を書いて、例えば:
123
3333
this CNewTest.
このように呼び出しても:
std::functionは実際の応用では、callbackパラメータを持つ関数があると仮定し、いくつかのタスクが完了すると、callというcallback関数があります.
次のように呼び出すことができます.
出力:
1
this CNewTest.
2
this CNewTest.
3
this CNewTest.
前述したようにstd::functionはnew可能です.つまり、このような関数を設計することができます.
次のように呼び出されます.
shared_ptrは私たちのnewのstd::functionが安全なdeleteであることを保証します.
この針はあちこちに投げてもいいです...C#の依頼より自由度が大きい.
std::function自体はマルチキャストの実現がなく、std::vectorなどを自分で包んで自分で作ることができます.
C#を使ったことがなければ、ここで簡単に依頼を説明しますが、使ったことがあれば、次はskipでいいです.
依頼はメソッド宣言であり,C言語では関数ポインタでアドレスを表すCALLメソッドが可能であり,依頼はC#でも少なくないことが分かっている.
しかし、依頼にはいくつかの違いがあります.主なところは、C++の中で、
関数ポインタは「オブジェクト向け」ではありません.たとえば、クラスCTestがあり、クラスにメンバーメソッドfooがあります.この場合、関数ポインタでfooを呼び出すには、fooがクラスCTestのメンバーであるため、CTestのインスタンスthisポインタが必要です.
たとえば、
class CTest
{
public:
void foo(int i) {}
};
メンバー関数ポインタは、次のように書く必要があります.void (CTest::*FuncFoo)(int);
このメンバーがconst修飾である場合、次のようになります.void (CTest::*FuncFoo)(int) const;
呼び出しはどうしますか?次のようにします.
CTest* pThis = new...;
FuncFoo pThis_foo = &CTest::foo;
(pThis->*pThis_foo)(123);
そう書けばpThis->*pThis_foo(123);
は、->*という演算子の優先度が低いため、問題が発生します...もしCTestがHEAPになかったら、stackにあったのでしょうか?
次のようにします.
CTest pThis;
(pThis.*pThis_foo)(123);
これらの奇抜な文法は,->*,.*この不思議な演算子は本当に卵が痛い.
スレッドを起動するとき、static関数からこのようなsbの構文を通じて、クラスthisをパラメータとして渡し、クラスのメンバー関数を走るのが一般的です.
でもC#では、
委任はオブジェクト向けで、クラスインスタンスのメンバー関数ポインタを委任で表すことができます.
例:
namespace ConsoleApplication1
{
public delegate void CallbackFunc(int i);
//void (callback*)(int i);
class CNewTest
{
private static readonly string _str = "this CNewTest.";
public void MemberCallback(int i)
{
System.Diagnostics.Debug.WriteLine(i.ToString());
System.Diagnostics.Debug.WriteLine(_str);
}
}
class Program
{
static void StaticCallback(int i)
{
System.Diagnostics.Debug.WriteLine(i.ToString());
}
static void Main(string[] args)
{
CallbackFunc StaticCbF = new CallbackFunc(StaticCallback);
StaticCbF(123); //Call to static void StaticCallback(int i)
CNewTest c = new CNewTest();
CallbackFunc MembCbF = new CallbackFunc(c.MemberCallback);
MembCbF(1234); //Call to c->MemberCallback
Console.ReadKey();
}
}
}
出力:123
1234
this CNewTest.
つまり、C#では、メソッドが依頼声明に適応すれば、callを呼び出すことができ、C#は自動的にthisポインタを隠してくれます.
したがって,C#にイベントが誕生し,1つのプロバイダオブジェクトが1つの依頼を定義し,依頼をイベントとして宣言すると,他のリスナーオブジェクトがこのイベントを自分のメンバー関数に関連付けることができ,プロバイダが必要に応じてこの依頼を呼び出すのを待つと,メンバー関数が呼び出され,イベントがトリガーされる.
なぜ事件と宣言し、依頼を直接暴露すればいいのではないでしょうか.
これは論理的な問題で、直接依頼を暴露すれば、購読者は自発的に依頼をトリガーすることができ、購読者は自発的に、また「購読」することができます.
イベントを使用する場合、サブスクライバはプロバイダが依頼をトリガーするのを待つしかなく、この場合、サブスクライバは「受動的」になるしかありません.
この概念は、C++で理解されています.つまり、
クラスAにはpublicのptr、ptr_がありますthis、このptrはメンバー関数ポインタを保存します.ptr_thisはptrのクラスインスタンスを保存します.
類Bはこのptrを自分のptrに変えた.ptr_thisをthisに変更し、このメンバー関数ポインタの宣言に適応します.
クラスAはいくつかの仕事が終わった後、ptrをチェックします!=nullptr,callというptrはクラスBのメンバ関数に呼び出される.
しかしC++はこれらの天然の不足があって、ptrはnullptrではありませんてむしろできて、しかしクラスBはとっくにdeleteされたかもしれなくて、しかしあなたのクラスAのptrに保存して依然としてnullptrではありませんて、あなたのcallは直接掛けて、もちろんC 11のshared_を使いますptr、weak_ptrというものは、解決策でもありますが、総じて言えば、不快で、これはどんなに面倒ですか.
つまり、C++では、メモリを手動で管理する輪から抜け出すことはできません.
C#のGC自動管理はこれらの問題を完全にクリアします.
もう1つは、C#の中で、依頼はマルチキャストの実現です.例えば:
static void Main(string[] args)
{
//CallbackFunc StaticCbF = new CallbackFunc(StaticCallback);
//StaticCbF(123); //Call to static void StaticCallback(int i)
//CNewTest c = new CNewTest();
//CallbackFunc MembCbF = new CallbackFunc(c.MemberCallback);
//MembCbF(1234); //Call to c->MemberCallback
CallbackFunc cbF = new CallbackFunc(StaticCallback);
CNewTest c = new CNewTest();
cbF += c.MemberCallback;
cbF(123);
Console.ReadKey();
}
出力:123
123
this CNewTest.
简単に言えば、cbFというものの中に、汎用的なリストがあります...Callの時は、ランニングメーターが1つずつcallが来ていて、とても便利でした.
はっきり言って、依頼は1つの【方法のインタフェース】で、方法が正しければいいので、安全で気持ちがいいです.
そこで依頼+イベントというマルチキャストを実現することで,観察者モードをうまく作ることができる.
そんなに多くの弊害が出てきて、C 11委員会もこれらの問題を発見して、std::function、これは、簡単に言えば、それは依頼の機能を実現するのにあまり役に立たない(事件を実現するのではありません).
やはり上のC#に書いてあるConsoleApplication 1に戻って、std::functionでもう一度やり直します.
(コンパイル環境はVS 2013、Windows SDK.)
std::functionを使用する前に、include
関数を用意します.
class CNewTest
{
char m_str[128];
public:
void MemberCallback(int i)
{
strcpy(m_str,"this CNewTest.
");
printf("%d
",i);
printf(m_str);
}
};
void StaticCallback(int i)
{
printf("%d
",i);
}
直接的な関数、クラスメンバー関数です.
まず、StaticCallbackを呼び出します.
int main()
{
std::function<void (int)> fp = std::function<void (int)>(&StaticCallback);
fp(123);
getchar();
return 0;
}
出力:123このように書くのは少し多いですね.うん、私たちはC++で、typedefがあります.
int main()
{
typedef std::function<void (int)> declare_fp;
declare_fp fp = declare_fp(&StaticCallback);
fp(123);
getchar();
return 0;
}
typedefは1行のコードが多くて、しかも直観的ではありませんて、恐れないで、私达はautoがあります.
int main()
{
auto fp = std::function<void (int)>(&StaticCallback);
fp(123);
getchar();
return 0;
}
実際の使用では、状況に応じてautoかtypedefかを決め、両者を組み合わせたほうがよい.(
std::functionはlamdbaという匿名関数と組み合わせて使用することもできます.逆天レベルの効果があります.ここではあまり詳しくありません.私は後でlamdbaの文章を書くかもしれません.)
このtmdの依頼がC#に似ているような気がしますか?しかし、C#の依頼はnewで、私たちのC++のは直接stackの上で開くことができて、もちろん私たちのnew、同じくできます.
int main()
{
typedef std::function<void (int)> declare_fp;
declare_fp* fp = new declare_fp(&StaticCallback);
(*fp)(123);
delete fp; //
getchar();
return 0;
}
次に、呼び出しメンバー関数ポインタをテストします.
int main()
{
auto fp = std::function<void (int)>(&StaticCallback);
fp(123);
CNewTest* c = new CNewTest();
auto fp2 = std::bind(&CNewTest::MemberCallback,c,std::placeholders::_1);
fp2(1234);
delete c;
getchar();
return 0;
}
出力:123
1234
this CNewTest.
std::bindを使用してstd::functionを生成し、このfunctionを呼び出すと過去のメンバー関数を呼び出すことができます.
今私たちのCNewTestはHeapにいます.もし私たちがstackにいたら?きっと思いついたでしょう.
CNewTest c;
auto fp2 = std::bind(&CNewTest::MemberCallback,&c,std::placeholders::_1);
fp2(1234);
多くの人がおかしいかもしれませんが、後ろのstd::placeholders::1どういう意味ですか.
実はbindの上のこの関数の方法を表すのは1つのパラメータがあって、このパラメータは汎型で、もし私たちがそれに汎型のパラメータを生成させないならば、直接1つの絶対値を書いて、例えば:
CNewTest* c = new CNewTest();
auto fp2 = std::bind(&CNewTest::MemberCallback,c,3333);
fp2();
の出力は次のとおりです.123
3333
this CNewTest.
このように呼び出しても:
CNewTest* c = new CNewTest();
auto fp2 = std::bind(&CNewTest::MemberCallback,c,3333);
fp2(1234);
出力は依然として上であり、デフォルトパラメータを強制する役割と理解できる.std::functionは実際の応用では、callbackパラメータを持つ関数があると仮定し、いくつかのタスクが完了すると、callというcallback関数があります.
void DoWork(std::function<void (int)> callback)
{
//DoWork....
int result[] = {1,2,3};
for (int i : result)
callback(i);
}
次のように呼び出すことができます.
int main()
{
CNewTest c;
auto fp2 = std::bind(&CNewTest::MemberCallback,&c,std::placeholders::_1);
DoWork(fp2);
getchar();
return 0;
}
出力:
1
this CNewTest.
2
this CNewTest.
3
this CNewTest.
前述したようにstd::functionはnew可能です.つまり、このような関数を設計することができます.
void DoWork2(std::shared_ptr<std::function<void (int)>> callback)
{
//DoWork2....
int result[] = {1,2,3};
for (int i : result)
(*callback.get())(i);
}
次のように呼び出されます.
int main()
{
{
CNewTest c;
auto fp = std::make_shared<std::function<void (int)>>
(std::function<void (int)>(std::bind(&CNewTest::MemberCallback,&c,std::placeholders::_1)));
DoWork2(fp);
//delete fp.
}
getchar();
return 0;
}
shared_ptrは私たちのnewのstd::functionが安全なdeleteであることを保証します.
この針はあちこちに投げてもいいです...C#の依頼より自由度が大きい.
std::function自体はマルチキャストの実現がなく、std::vectorなどを自分で包んで自分で作ることができます.