私はこの記事を読みましたが、メンバー関数へのポインターを呼び出すときは、インスタンス(1つへのポインターまたはスタック参照のいずれか)が必要であり、次のように呼び出すことができます。
(instance.*mem_func_ptr)(..)
or
(instance->*mem_func_ptr)(..)
私の質問はこれに基づいています:インスタンスがあるので、次のようにメンバー関数を直接呼び出してみませんか?
instance.mem_func(..) //or: instance->mem_func(..)
メンバー関数へのポインターの合理的/実用的な使用法は何ですか?
[編集]
私はX-developmentで遊んでいて、ウィジェットを実装する段階に達しました。Xイベントをクラスとウィジェットに変換するためのevent-loop-threadは、それらのイベントが到着したときに、各ウィジェット/ウィンドウのスレッドを開始する必要があります。これを適切に行うには、クラスのイベントハンドラーへの関数ポインターが必要だと思いました。
そうではありません。私が発見したのは、仮想基本クラスを使用するだけで、同じことをはるかに明確でわかりやすい方法で実行できるということでした。メンバー関数へのポインタはまったく必要ありません。上記を開発しているときに、メンバー関数へのポインターの実用性/意味について疑問が生じました。
member-function-pointerを使用するためにインスタンスへの参照が必要であるという単純な事実は、インスタンスの必要性を廃止します。
[編集-@sbiなど]
これが私のポイントを説明するためのサンプルプログラムです:(特に'Handle_THREE()'に注意してください)
#include <iostream>
#include <string>
#include <map>
//-----------------------------------------------------------------------------
class Base
{
public:
~Base() {}
virtual void Handler(std::string sItem) = 0;
};
//-----------------------------------------------------------------------------
typedef void (Base::*memfunc)(std::string);
//-----------------------------------------------------------------------------
class Paper : public Base
{
public:
Paper() {}
~Paper() {}
virtual void Handler(std::string sItem) { std::cout << "Handling paper\n"; }
};
//-----------------------------------------------------------------------------
class Wood : public Base
{
public:
Wood() {}
~Wood() {}
virtual void Handler(std::string sItem) { std::cout << "Handling wood\n"; }
};
//-----------------------------------------------------------------------------
class Glass : public Base
{
public:
Glass() {}
~Glass() {}
virtual void Handler(std::string sItem) { std::cout << "Handling glass\n"; }
};
//-----------------------------------------------------------------------------
std::map< std::string, memfunc > handlers;
void AddHandler(std::string sItem, memfunc f) { handlers[sItem] = f; }
//-----------------------------------------------------------------------------
std::map< Base*, memfunc > available_ONE;
void AddAvailable_ONE(Base *p, memfunc f) { available_ONE[p] = f; }
//-----------------------------------------------------------------------------
std::map< std::string, Base* > available_TWO;
void AddAvailable_TWO(std::string sItem, Base *p) { available_TWO[sItem] = p; }
//-----------------------------------------------------------------------------
void Handle_ONE(std::string sItem)
{
memfunc f = handlers[sItem];
if (f)
{
std::map< Base*, memfunc >::iterator it;
Base *inst = NULL;
for (it=available_ONE.begin(); ((it != available_ONE.end()) && (inst==NULL)); it++)
{
if (it->second == f) inst = it->first;
}
if (inst) (inst->*f)(sItem);
else std::cout << "No instance of handler for: " << sItem << "\n";
}
else std::cout << "No handler for: " << sItem << "\n";
}
//-----------------------------------------------------------------------------
void Handle_TWO(std::string sItem)
{
memfunc f = handlers[sItem];
if (f)
{
Base *inst = available_TWO[sItem];
if (inst) (inst->*f)(sItem);
else std::cout << "No instance of handler for: " << sItem << "\n";
}
else std::cout << "No handler for: " << sItem << "\n";
}
//-----------------------------------------------------------------------------
void Handle_THREE(std::string sItem)
{
Base *inst = available_TWO[sItem];
if (inst) inst->Handler(sItem);
else std::cout << "No handler for: " << sItem << "\n";
}
//-----------------------------------------------------------------------------
int main()
{
Paper p;
Wood w;
Glass g;
AddHandler("Paper", (memfunc)(&Paper::Handler));
AddHandler("Wood", (memfunc)(&Wood::Handler));
AddHandler("Glass", (memfunc)(&Glass::Handler));
AddAvailable_ONE(&p, (memfunc)(&Paper::Handler));
AddAvailable_ONE(&g, (memfunc)(&Glass::Handler));
AddAvailable_TWO("Paper", &p);
AddAvailable_TWO("Glass", &g);
std::cout << "\nONE: (bug due to member-function address being relative to instance address)\n";
Handle_ONE("Paper");
Handle_ONE("Wood");
Handle_ONE("Glass");
Handle_ONE("Iron");
std::cout << "\nTWO:\n";
Handle_TWO("Paper");
Handle_TWO("Wood");
Handle_TWO("Glass");
Handle_TWO("Iron");
std::cout << "\nTHREE:\n";
Handle_THREE("Paper");
Handle_THREE("Wood");
Handle_THREE("Glass");
Handle_THREE("Iron");
}
{編集]上記の例の直接呼び出しに関する潜在的な問題:
Handler_THREE()では、メソッドの名前をハードコーディングする必要があります。メソッドに変更を適用するには、メソッドが使用されている場所で変更を強制します。メンバー関数へのポインターを使用して行われる唯一の追加の変更は、ポインターが作成される場所です。
[編集]回答から収集した実用的な使用法:Chubsdadによる
回答
から:内容:
専用の「呼び出し元」関数を使用してmem-func-ptrを呼び出します。利点:他のオブジェクトによって提供される関数を使用してコードを保護する方法:特定の関数が多くの場所で使用され、名前やパラメーターが変更された場合、割り当てられた名前を変更するだけで済みます。ポインタを指定し、「呼び出し元」関数で呼び出しを調整します。(関数をinstance.function()として使用する場合は、どこでも変更する必要があります。)
Matthew Flaschenによる回答から:内容:
クラスのローカルスペシャライゼーション
利点:コードをはるかに明確、シンプル、使いやすく、保守しやすくします
方法:複雑なロジックを使用して従来実装されていたコードを(潜在的に)大きなswitch()/ifに置き換えます-次に、スペシャライゼーションへの直接ポインタを含むステートメント。上記の「発信者」関数とかなり似ています。