ページ 1 / 1
違うclass間のメンバー関数の代入に関する質問
Posted: 2007年8月22日(水) 14:54
by まだ
暑中お見舞い申し上げます
あるクラスのobjectから別のクラスに関数のポインターを設定したいですが、
繰り返し試してみたが、いつもコンパイラエラーでした。
具体に、クラスclassAのコンストラクタの中に
クラスBのobjectを生成します。
classA{
void (*funcp)(int,char);
};
classB{
public:
void bfunc(int i, char c)
{
.....
}
};
classA:: classA{
classB *objb = new classB();
funcp = objb->bfunc;
}
これでうまくできなかったし、ほかのやりかたもだめだったんです。
宜しくお願い申し上げます
Re:違うclass間のメンバー関数の代入に関する質問
Posted: 2007年8月22日(水) 16:36
by Justy
classB::bfuncは「メンバ関数」であって、「関数」ではありません。
なので、根本的に関数へのポインタである funcpに入れることはできません。
そこで、このメンバ変数を関数へのポインタから classBクラスのメンバ関数へのポインタに
修正する必要があります。
又、他にもメンバ関数へのポインタの取り出し方とかも間違っています。
[color=#d0d0ff" face="monospace]
#include <iostream>
class classB
{
public:
void bfunc(int, char)
{
std::cout << "classB::bfunc()" << std::endl;
}
};
class classA
{
public:
classA()
{
std::cout << "classA::classA()" << std::endl;
m_pb = new classB();
m_funcp = &classB::bfunc;
}
void callB(int i, char c)
{
std::cout << "classA::callB()" << std::endl;
if(m_pb && m_funcp)
(m_pb->*m_funcp)(i, c);
}
private:
classB * m_pb;
void (classB::*m_funcp)(int,char);
};
int main(int , char *[/url])
{
classA a;
a.callB(100, 'A');
return 0;
}[/color]
Re:違うclass間のメンバー関数の代入に関する質問
Posted: 2007年8月22日(水) 16:38
by Blue
>void (*funcp)(int,char);
関数の型が違います。(static関数ならあっているけど。)
また仮に代入できたとしても、実行時にはclassBのインスタンスが必要になります。
(だったら、関数ポインタにして入れる必要性がなさそうかと)
>classB *objb = new classB();
objbはいつdeleteするのでしょうか?
メンバ変数にしてなくてもいい?
一応類似スレ
http://rararahp.cool.ne.jp/cgi-bin/lng/ ... 070008.txt
一応適当に。
#include <iostream>
class B
{
public:
void func(int i, char c) { std::cout << "B::func" << std::endl; }
};
class A
{
B m_b;
void (B::*m_func)(int, char);
public:
A() : m_func(m_b.func) {}
void Test() { (m_b.*m_func)(0, 0); }
};
int main()
{
A a;
a.Test();
return 0;
}
追記)
むっちゃかぶりました。orz
Re:違うclass間のメンバー関数の代入に関する質問
Posted: 2007年8月22日(水) 17:29
by まだまだ
さっそくご返答ありがとうございます。
こちらの説明不足で申し訳ございませんが、
関数の所属classについてclassAは予め分かるわけではないのです。
(そもそも、「無所属」のグローバル的な関数かもしれません)
したがって、classAの中にclassBに関する情報を入れるのは不可能です。
条件を改めてから
もう一度ご教授お願いできれば幸いと思います。
Re:違うclass間のメンバー関数の代入に関する質問
Posted: 2007年8月22日(水) 17:39
by Blue
C++の言語仕様上無理そうだけど。
# VS2005のC++/CLIのデリゲードであれば可能なのになぁと。
>classB{
>
>public:
>void bfunc(int i, char c)
>{
>.....
>}
>
>};
のbfuncはBのインスタンスを必要としない(メンバ変数やメンバ関数を使わない)のであれば
static関数化してしまえば実現は可能でしょうけど。(というのはもう既に指摘済み)
Re:違うclass間のメンバー関数の代入に関する質問
Posted: 2007年8月22日(水) 17:56
by Blue
別案として引数としてインスタンスを渡すようにして、使う側でキャストするとか。
(というかこんなのだったら既定クラスを作って仮想関数呼ぶほうが普通だろうと。)
#include <iostream>
class A
{
public:
void (*m_func)(void* data);
void Exec(void* data)
{
(*m_func)(data);
}
};
class B
{
public:
static void func(void* data)
{
((B*)data)->Hoge();
}
private:
void Hoge()
{
std::cout << "B::Hoge" << std::endl;
}
};
class C
{
public:
static void func(void* data)
{
((C*)data)->Hoge();
}
private:
void Hoge()
{
std::cout << "C::Hoge" << std::endl;
}
};
int main()
{
A a;
B b;
C c;
a.m_func = b.func;
a.Exec(&b);
a.m_func = c.func;
a.Exec(&c);
return 0;
}
Re:違うclass間のメンバー関数の代入に関する質問
Posted: 2007年8月22日(水) 18:06
by Justy
>というかこんなのだったら既定クラスを作って仮想関数呼ぶほうが普通だろうと
これがベストでしょうね。
>そもそも、「無所属」のグローバル的な関数かもしれません)
>したがって、classAの中にclassBに関する情報を入れるのは不可能です
つまり呼び出し時の引数と戻り値が同じだけど、グローバルな関数かもしれないし
クラスのメンバかもしれない、と。
んー、ポインタではないですが、
http://www.boost.org/を使えば見た目近いことは
出来なくはないですが、さすがにちょっと・・・。
[color=#d0d0ff" face="monospace]#include <iostream>
#include <boost/function.hpp>
#include <boost/bind.hpp>
class classB1
{
public:
void b1func(int i, char c)
{
std::cout << i << " " << c << " classB1::bfunc()" << std::endl;
}
};
class classB2
{
public:
void b2func(int i, char c)
{
std::cout << i << " " << c << " classB2::bfunc()" << std::endl;
}
};
void func(int i, char c)
{
std::cout << i << " " << c << " ::func()" << std::endl;
}
class classA
{
public:
template <typename T>
void register_function(T func)
{
m_function = func;
}
void call(int i, char c)
{
if(m_function)
m_function(i, c);
}
private:
boost::function<void (int , char)> m_function;
};
int main(int , char *[/url])
{
classA a;
a.register_function(&func);
a.call(1, 'A');
classB1 b1;
a.register_function(boost::bind(&classB1::b1func, &b1, _1, _2));
a.call(2, 'B');
classB2 b2;
a.register_function(boost::bind(&classB2::b2func, &b2, _1, _2));
a.call(3, 'C');
return 0;
}[/color]
実行すると
[color=#d0d0ff" face="aria[/url]
1 A ::func()
2 B classB1::bfunc()
3 C classB2::bfunc()[/color]
となります。
Re:違うclass間のメンバー関数の代入に関する質問
Posted: 2007年8月22日(水) 19:44
by まだまだ
Blue様、Justy様
丁寧なご指導本当にありがとうございます。
どの例も大変参考になります。
なぜこんなことをやれたいといいますと、
classAは基本機能の提供者として、いろんな不特定のクライアントに利用されたいです。
ただ、ほんの一部の処理はクライアント側に自由に定義できるようにしなければなりません。
この「ほんの一部の処理」を関数の形でまとめて、各クライアント自身が用意しております。
classAとクライアントclass B, calss C, class D...の間は処理レベルのinterface (即ち関数)しかお互い知らないので、
お互いに継承関係になりません(作れません)。
main()関数の中で、はじめてお互いが関数レベル(処理レベル)での交流(関数ポインターの引渡し)ができるようにしたいですが、、、
ごく普通な要望だと思いましたが、C++という超パワフルな言語はこんなに苦手?
知りませんでした。
Justy様へ
boostをご紹介下さって、そして詳しい例コードも組んでいただきまして
非常に感謝します。
ただ、私はすぐboostを利用できるようなレベルになっていないし、時間かけて勉強する必要です。
また何かアイディアあれば是非お願いします。
Re:違うclass間のメンバー関数の代入に関する質問
Posted: 2007年8月22日(水) 23:08
by Justy
>ごく普通な要望だと思いましたが、C++という超パワフルな言語はこんなに苦手?
どういう状況なのか今ひとつわかりませんが、関数は関数ポインタだけで済みますが、
メンバ関数はインスタンスとメンバ関数ポインタの2つが必要になりますし、C++は型が強い(?)ので
なかなか共通化するのは面倒なんですよね。
>また何かアイディアあれば是非お願いします
が、手がないわけではありません。
これならどうでしょう?
仮想関数をうまく使って、メンバ関数タイプと関数タイプと内部では分けていますが、
インターフェースは共通化されています。
[color=#d0d0ff" face="monospace]
#include <iostream>
// ----- テスト用クラス・関数
class classB1
{
public:
void b1func(int i, char c)
{
std::cout << i << " " << c << " classB1::bfunc()" << std::endl;
}
};
class classB2
{
public:
void b2func(int i, char c)
{
std::cout << i << " " << c << " classB2::bfunc()" << std::endl;
}
};
void NormalFunc(int i, char c)
{
std::cout << i << " " << c << " ::NormalFunc()" << std::endl;
}
// ----- 汎用呼び出しクラス
class Caller
{
public:
// コンストラクタ
Caller() : m_holder() {}
// デストラクタ
~Caller()
{
delete m_holder;
}
// メンバ関数設定
template <class T>
void SetFunction(T &instance, void (T::*func)(int, char))
{
delete m_holder;
m_holder = new MFuncHolder<T>(instance, func);
}
// 関数設定
void SetFunction(void (*func)(int, char))
{
delete m_holder;
m_holder = new FuncHolder(func);
}
// 呼び出し
void Execute(int i, char c)
{
if(m_holder)
m_holder->Execute(i, c);
}
private:
class HolderBase
{
public:
virtual ~HolderBase()
{
}
virtual void Execute(int, char) = 0;
};
// メンバ関数用
template<class T>
class MFuncHolder
: public HolderBase
{
public:
MFuncHolder(T &instance, void (T::*func)(int, char))
: m_instance(instance), m_pMFunc(func)
{
}
public:
virtual void Execute(int i, char c)
{
if(m_pMFunc)
(m_instance.*m_pMFunc)(i, c);
}
private:
T& m_instance;
void (T::*m_pMFunc)(int, char);
MFuncHolder& operator=(const MFuncHolder &);
};
// 関数用
class FuncHolder
: public HolderBase
{
public:
FuncHolder(void (*func)(int, char))
: m_pFunc(func)
{
}
public:
virtual void Execute(int i, char c)
{
if(m_pFunc)
(*m_pFunc)(i, c);
}
private:
void (*m_pFunc)(int, char);
};
HolderBase * m_holder;
// 一応コピー禁止
Caller& operator=(const Caller &);
Caller(const Caller &);
};
int main(int , char *[/url])
{
Caller caller;
caller.SetFunction(&NormalFunc);
caller.Execute(1, 'A');
classB1 b1;
caller.SetFunction(b1, &classB1::b1func);
caller.Execute(2, 'B');
classB2 b2;
caller.SetFunction(b2, &classB2::b2func);
caller.Execute(3, 'C');
return 0;
}
[/color]
Re:違うclass間のメンバー関数の代入に関する質問
Posted: 2007年8月23日(木) 00:05
by Blue
質問者ではないですが、カナリ勉強になりました。
VC++6.0じゃ
>void (T::*m_pMFunc)(int, char);
が理解できないようですね。
(VS2005でビルド/実行しました。
ほかのコンパイラは昔は入れていたけどOS入れなおしたときに面倒になって入れていないw)
Re:違うclass間のメンバー関数の代入に関する質問
Posted: 2007年8月23日(木) 00:22
by Justy
あー、うちは 7.1で、6.0は持ってないので判りませんが・・・6.0では厳しいかもしれませんねぇ。
Re:違うclass間のメンバー関数の代入に関する質問
Posted: 2007年8月23日(木) 00:29
by Blue
ついでに、boost::functionも6.0じゃ理解できなかったです。
boost::function2<void, int , char>
として対応。
Re:違うclass間のメンバー関数の代入に関する質問
Posted: 2007年8月23日(木) 00:31
by Justy
> ついでに、boost::functionも6.0じゃ理解できなかったです。
たしかに。
# あ、あと本格的に使うなら operator=の定義とか、コンストラクタ周りとか、
CV修飾されたメンバ関数対応とかもしないといけないですね。
Re:違うclass間のメンバー関数の代入に関する質問
Posted: 2007年8月23日(木) 10:50
by たかぎ
やりたいことの確認ですが、classAと他のクラスは動的に結びつける必要があるのでしょうか?
静的に結びつけるだけであれば、Boost C++ Librariesを使わなくても、標準ライブラリのmem_funファミリやbind1st/bind2nd等を駆使すればどうにかなります。
動的にやりたいなら、原則としてコールバック側を仮想関数にするしかありません(もっと強引な方法はありますが...)。
デリゲートと同じことをしたいなら、boost::signalsを使うのが一番です。
> ただ、私はすぐboostを利用できるようなレベルになっていないし、時間かけて勉強する必要です。
リファレンスを読む手間を惜しむなら、何倍もの労力を払って自力で実装するしかありません。
急ぎであれば、なおさら既存のライブラリを使うべきです。
> ごく普通な要望だと思いましたが、C++という超パワフルな言語はこんなに苦手?
> 知りませんでした。
苦手というより、やりたいことがはっきりしないので、適切なアドバイスが出来ないだけでは?
それから、デリゲートはC++では必ずしも普通の要望ではありません。
Re:違うclass間のメンバー関数の代入に関する質問
Posted: 2007年8月23日(木) 11:15
by たかぎ
一応、静的にも動的にもなるように考えてみました。
望んでいることと違うようならご指摘ください。
#include <cstdio>
class B
{
public:
void bfunc(int i, char c)
{
std::printf("B::bfunc(%d, %c);\n", i, c);
}
};
void bfunc(int i, char c)
{
std::printf("::bfunc(%d, %c);\n", i, c);
}
class defalt_policy
{
protected:
~defalt_policy() {}
};
class polymorphic_A
{
public:
virtual void operator()(int i, char c) = 0;
virtual ~polymorphic_A() {}
};
template<class T, class P = defalt_policy>
class A : public P
{
T o_;
void (T::*pmf_)(int, char);
public:
explicit A(void (T::*pmf)(int, char)) : pmf_(pmf) {}
void operator()(int i, char c) { (o_.*pmf_)(i, c); }
};
template<class P>
class A<void, P> : public P
{
void (*pf_)(int, char);
public:
explicit A(void (*pf)(int, char)) : pf_(pf) {}
void operator()(int i, char c) { pf_(i, c); }
};
int main()
{
A<B> a1(&B::bfunc);
a1(123, 'a');
A<void> a2(&::bfunc);
a2(456, 'b');
polymorphic_A* pa1 = new A<B, polymorphic_A>(&B::bfunc);
(*pa1)(100, 'c');
delete pa1;
polymorphic_A* pa2 = new A<void, polymorphic_A>(&::bfunc);
(*pa2)(200, 'd');
delete pa2;
return 0;
}
これなら、後からいくらでも異なるクラスと関連付けられますし、非メンバ関数でも同じように扱えます(第1テンプレート引数にvoidを指定)。
Re:違うclass間のメンバー関数の代入に関する質問
Posted: 2007年8月23日(木) 15:19
by ま
Justy様、たかぎ様
大変お世話になっております。それに
みなさんのわざの高さにいつも感服します。
いただいているサンプルコードをいまの問題の解決に如何にうまく利用するかを
現在一生懸命に考えています。
本件に限らず、たくさんの英知に富んでいるコードを
これからも是非ご参考させていただきたいのです。
(大体分かったつもりですが、その真髄を完全に身につけるには少々時間かかりそうです)
boostに関してはいずれ利用したいのです。
C++だけではやはり物足りない感じ(MFCには少々抵触感あります)。
皆さんに心からお礼を申し上げます。