• ベストアンサー

オーバーライド関数の呼び出し元を判定するにはどうしたらよいでしょうか?

オーバーライド関数の呼び出し元を判定するにはどうしたらよいでしょうか? 基底クラスはCPropertyPageで、そこから複数階層まで継承しています。 末端クラスの関数が呼ばれたとき、それがどの派生クラスから呼ばれたかによって処理を分けたいのですがどうしたら良いでしょうか?

質問者が選んだベストアンサー

  • ベストアンサー
  • jacta
  • ベストアンサー率26% (845/3158)
回答No.6

#2です。 > GetObject()でなんとかならないかなぁと考えています。 こういうのは「型スイッチ」といって、典型的な禁じ手のひとつです。 どうしてもそれ以外の実現方法がないとか、禁じ手をおかすことで多大なメリットが得られるならともかく、今回の場合はそのようには思えません。 > 既存のソース(基底クラス)は出来ればあまり触りたくないので・・・ だったら非メンバの関数テンプレートを作るとか、もっとよい方法がいろいろあると思います。

hanaco222
質問者

お礼

> こういうのは「型スイッチ」といって、典型的な禁じ手のひとつです。 そうなのですね・・・ 継承しているクラスではいろんな人が手を加えて、ぐちゃぐちゃになっていて、出来れば触りたくないと考えてしまいました。 メンテのし易さも考え、今後方法を検討していきます。ありがとうございました。

全文を見る
すると、全ての回答が全文表示されます。

その他の回答 (5)

回答No.5

「親クラス」の定義が曖昧ですが、C++の基底クラスではないのですか? 状況がわかりませんので、基本的な方針を。 「呼び出し元の情報が欲しければ、呼び出し元でセットしてもらえ」 これが、基本です。 この場合だったら、FuncA(CObject *wk) という形にして、呼び出す方からセットしてもらえば、問題ありません。 もしも、スコープの関係か何かで、呼び出し元で、セットできないとしたら、「呼び出し元とそのオブジェクトの関係」をどう定義するかで、いずれにしても、関連する情報があるはずです。それをセットすればいいです。(オブジェクトの識別名とか)

hanaco222
質問者

お礼

分かりにくい説明に何度も回答頂きありがとうございます。 > この場合だったら、FuncA(CObject *wk) という形にして、呼び出す方からセットしてもらえば、問題ありません。 FuncAに該当する関数がたくさんあり、また色んなところから呼ばれているので影響範囲が大きいので頭が痛いです・・・ > 「呼び出し元の情報が欲しければ、呼び出し元でセットしてもらえ」 でもこれが【基本】なのですよね、頑張ってみます。ありがとうございました。

全文を見る
すると、全ての回答が全文表示されます。
回答No.4

No.3 です。もしも、「オーバーライドに限る」(仮想関数は使えない/使いたくない)というのであれば、基底クラス側を書き直す必要があります。 少なくとも、 CObject* 派生クラスA::GetObject(void) は、派生クラスA(または、そのさらに派生クラス)からしか呼び出されませんから、ここでどうあがいても無理です。 --- void 基底クラス::FuncA() { FuncB(objectA); } void 基底クラス::FuncB(CObject *pObj) { // FuncA の GetObject() 呼び出し以降に元々あった処理 …… } --- void 基底クラス::FuncA() { FuncB(objectAA); } } こんな感じです。

全文を見る
すると、全ての回答が全文表示されます。
回答No.3

一応、どのクラスから呼ばれたかを識別する方法はないことはありません。 ただ、言い換えると、「クラスが増えるたびに書き直しをしなければならない」ということで、普通はあまりいい方法ではありません。それに、派生クラスで定義した関数を、基底クラスから呼び出すことはできないはずですが。 設計が間違っていなければ、この場合、仮想関数にするのは、GetObject() の方です。 --- virtual CObject* 基底クラス::GetObject(void) { return objectA; } void 基底クラス::FuncA() { // FuncA() は、「ふさわしいオブジェクト」を作って…… CObject *pObj = GetObject(); // それ以降の「共通の処理」をする。 } --- virtual CObject* 派生クラスA::GetObject(void) { return (自分のクラスで返したいオブジェクト); } また、おそらくは、 --- virtual CObject* 基底クラス::GetObject(void) = 0; // 純粋仮想関数にする void 基底クラス::FuncA() { CObject *pObj = GetObject(); …… } --- virtual CObject* 派生クラスA::GetObject(void) { return (自分のクラスで返したいオブジェクト); } // 各クラスで、ふさわしいオブジェクトを返す のほうが、全体の設計がすっきるするはずです。

hanaco222
質問者

補足

すみません、私の説明不足です。 ・基底クラスというか親クラスです。 ・仮想関数は、GetObject()です。 派生クラスAのGetObject()が呼ばれたときは、その呼び出し元に応じて「objectAA」或いは「objectBB」を返したいです。 派生クラスA内の関数からGetObject()がコールされた場合は「objectAA」、親クラスからコールされたときは「objectBB」といった具合です。

全文を見る
すると、全ての回答が全文表示されます。
  • jacta
  • ベストアンサー率26% (845/3158)
回答No.2

それこそ仮想関数をオーバーライドすればよいのでは?

hanaco222
質問者

補足

補足します。 仰るように、FuncA()を派生クラスにも持てば良いのかもしれませんが GetObject()以降に続く処理が全く同じために(しかも長い)、 GetObject()でなんとかならないかなぁと考えています。 既存のソース(基底クラス)は出来ればあまり触りたくないので・・・ --- CObject* 基底クラス::GetObject(void) { return objectA; } void 基底クラス::FuncA() { CObject *pObj = GetObject(); ・・・ } --- CObject* 派生クラスA::GetObject(void) { if (...) { // 基底クラスから呼ばれたとき // objectAAを返したい return objectAA; } else if (...) { // 派生クラスから呼ばれたとき // objectAAを返したい return objectBB; } }

全文を見る
すると、全ての回答が全文表示されます。
回答No.1

 関数がオーバーライドされているのなら、クラスを指定して呼び出さない限りはそのクラス内のオーバーライド関数が呼び出されるだけだと思いますが。  オーバーライドされていないのなら継承クラスからは基底クラスの関数が呼び出されるでしょうけど。  以下、「オーバーライドでない」関数の呼び出し元を判定する方法。 (1)基底クラスから継承クラスまでそれぞれ固有の値を持つメンバ変数を用意する。 (2)継承される関数の引数にそのメンバ変数を渡すようにする。  以上で呼び出し元のクラスは判明するはずですが。

hanaco222
質問者

お礼

説明が悪くて申し訳ないです。 他の方への補足にあるとおりの状況です。 回答ありがとうございました。

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • std::listでオーバーライドはできますか?

    基底クラスのstd::listに、派生クラスを入れてオーバーライドはできますか? ポインタじゃないとダメ vectorだと途中で挿入すると後ろのポインタが書き換わる等 色々な意見を目にしたのですが...listだとどうなのでしょうか。

  • オーバーライト、それとも、オーバーライド?

    C++で、スーパークラスで定義されている関数を 継承したクラスで定義しなおすことをオーバーライトと いうのでしょうか? それとも、オーバーライドと いうのでしょうか? override と overwrite を辞書で調べてみたんですが、 どちらも、よく似た意味のようで…。 よろしくお願いします。

  • 継承元のpublicなメンバ関数を隠ぺいしたい

    C++です.(VC++2013) 親クラスではpublicで宣言されているメンバ関数があり,継承後はこのメンバ関数を隠ぺいしたいときはどのようにすればよいのでしょうか. 具体的には,std::vectorから派生したクラスを定義し,この独自クラスのインスタンスへ要素を追加する場合は,新しく独自に定義した関数を通じて行います. そのため,push_back()を呼べないようにしたいのです. 自分で作成した関数には,pushという名前をつけたいので,単純に自分の要素追加の関数をpush_backという名前でオーバーライドすることはできません. push関数内では,push_backした後,即座にsortしたいので,push_backをいう名前をつけたくないのです. とりあえずは,派生クラスでprivateにpush_backをオーバーライドし,自分で作った要素追加の関数で使うpush_backはstd::vectorのものを用いることを明示することでできました. 他にも何かやり方があれば,ご教授下さい.

  • オーバーライドとは?

    こんばんわ。 いまVB.Net2005でプログラミングをしている者です。 Overrideという機能がありますが, 使い方がよくわかりません。 継承元側で定義したプロシジャ(△△△△)と同じ名前のプロシジャを 派生先側で定義しました。 派生先側でInherits ○○○○として, 派生先側の当該プロシジャ内で, MyBase.△△△△として, さらに派生先側の独自の処理を続けました。 このような使い方でよいのでしょうか? なおこのような使い方をするときに, わざわざOverride句を指定する必要があるのでしょうか? 初歩的な質問になるかもしれませんが, 教えてください。よろしくお願いします。

  • 仮想関数でのオーバーライドでの質問

    仮想関数でのオーバーライドでの質問 二つの派生クラスを定義したのですが、一つ目のクラスのメソッドが実行されないで困っています。 下記にソースを載せておきます。 どこが原因でそうなるのかご指摘お願いします。 /* 継承 */ #include <iostream> using namespace std; class Var { private: int value; static Var* re; public: // 静的関数 static bool func() { // 内部で同一の関数を呼び出す re->show(); return true; } virtual show() { cout << "message00" << "moji" << endl; } // 自身のポインタを取得 Var() { re = this; } }; Var* Var::re = NULL; class Msg01 : public Var { private: int value; public: show(void) { cout << "message01" << "moji" << endl; } }; class Msg02 : public Var { private: int value; public: show(void) { cout << "message02" << "moji" << endl; } }; int main() { Msg01 val; Msg02 msva; val.func(); msva.func(); getchar(); return 0; } /------------------------------------------------------/ 実行結果 message02moji message02moji /------------------------------------------------------/ message01mojiが表示されるはずなんですが表示されないでいます。 Msg02を宣言したためこのようになってしまったんでしょうか?

  • オーバーライドをやめた時

    VC++6.0、SDIのスプリットウィンドウを使用しています。 リストビュークラスを2段階に継承している場合に、試しに一度孫クラス1にOnLButtonDownハンドラをオーバーライドして左ボタン押下時に実行されることをブレークポイントにて確認後、やっぱりもうひとつ上のクラスでオーバーライドしようと思い、子クラスにOnLButtonDownハンドラをオーバーライドし、孫クラス1からOnLButtonDownハンドラを削除したところ、左ボタンを押下しても子クラスの街頭ハンドラが呼ばれません。 ノータッチだった孫クラス2では思惑通りに子クラスのハンドラが呼ばれています。 孫クラス1に何か痕跡が残っていてそちらに引っ張られているのでしょうか? ハンドラの追加・削除は全て暮らすウィザードを使用しました。 どなたかわかる方、ご教示よろしくお願います。 class CKoListView : public CListView {  ・・・・ } class CMago1ListView : public CKoListView {  ・・・・ } class CMago2ListView : public CKoListView {  ・・・・ }

  • メンバ関数テンプレートの仮想関数。

    VC8.0言語処理系でメンバ関数テンプレートを仮想関数にしたらエラーになりました。 これは、VC8.0言語処理系の対応なのでしょうか? それともC++言語系の仕様なのでしょうか? 今回、メンバ関数の一部にイテレータを使用していまして、その関係で一部の関数がテンプレートになっています。 そして、基底クラスでは実装せず、派生先で実装を強制する純粋仮想関数としたのですが、この処理がコンパイルエラーとなっています。 メンバ関数の参照などを考えたのですが、どうも巧くコンパイルでき無そうです。 この処理が出来ないことは設計段階で把握していなかったので、これが出来ないとすると設計のし直し(テンプレートの廃止など)をしなくてはいけません。 詳しい方がいらっしゃいましたら、お願いします。

  • 個々の関数について継承しているかどうかを判定したいんですが、

    個々の関数について継承しているかどうかを判定したいんですが、 class CBase {public: virtual int A(){return 0;} virtual int B(){return 0;} }; これが規定クラスだとして、 class CTest : public CBase {public: int A(){return 1;} int B(){return 1;} }; のクラスが存在します。 一番有効だと思ってるのが、上記を変えて CBase :: virtual int A(){throw this_base; return 0;} のように書けば判定できると思いますが、例外を使うよりもいい方法はありませんか。

  • 多態性を利用して派生クラスの関数を呼びたい

    環境はVS2013,言語はC++です. 基底クラス側で,基底クラスのポインタを引数に受ける仮想関数を宣言し, 派生クラスでその実装をします. 派生クラス側で引数に派生クラスのポインタを受けて,派生クラスのメンバにアクセスできるようにしたいのです. 作成したサンプルを下記に示します. Hello()は関係ありません. また,これはあくまで簡略化したサンプルなだけなので,thisポインタからメンバにアクセスすればよいというものではありません. #include <iostream> class Base { public: int i; Base() :i(0) {} virtual void print(Base *p) { std::cerr << "Base : " << i << std::endl; } virtual void hello() = 0; }; class Super : public Base { public: int x; Super() : x(1) {} void print(Super *p) { std::cerr << "Super : " << p->x << std::endl; } void hello() { std::cerr << "hello" << std::endl; } }; int main(void) { Base *p; p = new Super; p->print(p); } 上記を実行した結果,Base::print()が呼び出されました. print関数の引数に派生クラスSuperの実態を差すBase型ポインタを与えたときに,Super::print()を呼び出せるようにするには,何か方法はありますか. p->print(p)の呼び出し部分をp->((Super*)p)にキャストしても結果は同じくBase::print()が呼ばれました. Base::print()の実装を無くした場合は,未解決の外部参照のコンパイルエラーが発生し, Base::print()を純粋仮想関数にした場合は,Super側でSuper::pirnt(Base*p)を実装しなければならないのか,抽象クラスのインスタンス化ができないというコンパイルエラーが発生します. これは,派生クラスとはいえ,引数リストの型が違うのでオーバーライドされていないということですよね. 引数をBase *p,Super *pからそれぞれvoid *pへ変更し,Super::print()内で(Super *)へキャストしなおしたところ,きちんとオーバーライドされ,うまくはいきました. しかしながら,この方法だと派生クラス側で実装する際に毎回キャスト処理を書かなくてはなりません. 他に何かきれいな方法はないでしょうか.

  • C++のクラスの仮想デストラクタについて

    C++のクラスの仮想デストラクタについて教えてください。 デストラクタは、クラスの名前の前にチルダを付けたものが名前になりますが、とあるクラスの継承クラスは、その親クラスとクラス名が違うので、デストラクタの名前も親クラスのものとは別になる。つまり。継承関係のあるクラスでもデストラクタはオーバーライドせず、各クラス毎に別の名前で存在する、ということになると思います。 ですので http://wisdom.sakura.ne.jp/programming/cpp/cpp31.html このページの下部にあるように、「デストラクタは、派生クラスから基本クラスへ向かって順番に呼び出される」というのもなんとなく合点が行きます。 しかし、仮想デストラクタというものがあることを知りました。 上記のようにデストラクタは継承関係のあるクラス間でも、それぞれクラス毎に作ればよいと思っていましたが、子クラスの方でオーバーライドする必要がある場合があるのでしょうか。あるとすれば、それはどんな場合なのでしょうか。 また、上記のURLでは、「C++ 言語のデストラクタはオーバーライドを行いません」と書いてあり、なんだかよく分からなくなってきました。仮想デストラクタというものが存在するのに? どなたか詳しい方いらっしゃいましたらご教示頂けると幸いです。