- ベストアンサー
親クラスのポインタで派生クラスの関数呼び出し
- 親クラスのポインタで派生クラスの関数を呼び出すと、派生クラスの関数が呼ばれることがあります。
- 派生クラスのポインタ型にキャストすると、派生クラスのメンバ関数が利用可能になります。
- ただし、親クラスのオブジェクトを指すポインタ経由で派生クラスのメンバ関数を呼び出す場合、派生クラスのコンストラクタが呼ばれないため注意が必要です。
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
>前の方のお礼コメントにも書きましたが、func2が呼べるのが謎です。 謎も何も、そのC++処理系がそのようなコードを出力する処理系だというだけの事です。 例えばint CSub::func()が次のようなCソースと同等にコンパイルされ int CSub_func2(CSub *this) { return this->m_nSub1; } CSub::func2()の呼び出しが nRet = pSub->func2();// (*4) ↓ nRet = CSub_func2(pSub); のようなコードと同等ならばpSubの値がCParentのインスタンスだろうがCSubのインスタンスだろうが関係なくCSub::func2()が呼び出されます。
その他の回答 (4)
- m-take0220
- ベストアンサー率60% (477/782)
> pSubポインタの指し示す先にあるオブジェクトの中のメンバ関数テーブルにfunc2はないはずだから、 関数テーブルは、仮想関数のようにオブジェクトによって呼び出す関数が決まる仕組みを実装するためのもので、すべての関数がテーブルを使って呼び出されるわけではありません。そうでないと、派生クラスの仮想関数から基底クラスの同名関数を呼び出しできなくなりますよね。
お礼
そうですね。仮想関数でない普通のメンバ関数はコンパイル時に静的に呼び出すべき関数が決まるから、オブジェクトに関数テーブルを持っているのではないですね。 本来このような関数呼び出しはコンパイル時に警告が出ても良さそうですが、それが普通に通るのがC++なんですかね。
- επιστημη(@episteme)
- ベストアンサー率46% (546/1184)
> CParentの実体を指しているポインタ経由でCSubにしかないメンバ関数をなぜ呼び出せるのでしょう? どんな挙動を示そうが、未定義な挙動に理由はありません。 ...とはいえ、一般的な実装では以下のようなことが起きています. int CSub_func2(CSub* this) { ... } // int CSub::func2() int main() { CParent parent; CParent* pParent = &parent; CSub* pSub = (CSub*)pParent; CSub_func2(psub); // pSub->func2() と等価 }
- m-take0220
- ベストアンサー率60% (477/782)
> なぜこの呼び出しで子クラスの関数を呼ぶことが出来るのでしょうか? ポインタをキャストした場合、元のオブジェクトが何かはチェックされません。 pSubはCParentだから、func2という関数は存在しないはず、とお考えかもしれませんが、コンパイラ的にはCSubクラスのfunc2という関数を呼び出し、その際にthisにpSubを設定しているだけです。 不定値が返るのは、pSubがCSubのポインタだとするとm_nSub1があるはずの位置のデータを返すからです。SetDataは期待通り動作しているように見えますが、pSubがCSubのポインタだとするとm_nSub2があるはずの位置のデータを書き換えています。実際にはpSubはCParentですから、m_nSub2の場所は確保されていないので、何か別のデータが保存されている部分を書き換えていることになります。コンストラクタを通っているかどうかという問題ではありません。 CParentのポインタがあるとして、それが実際にはCSubかCSubの派生クラスのポインタとして使用して問題ないのかを確認したいのであれば、dynamic_castを使用することになるんじゃないかと思います。
お礼
ご回答ありがとうございます。 前の方のお礼コメントにも書きましたが、func2が呼べるのが謎です。CSubクラスのfunc2の実体はどのタイミングでメモリ上にロードされるのか? func2の呼び出しより前にロードされているとして、なぜCParentを指しているポインタ経由ででCSub::func2を正しくポイント出来るのか?
- επιστημη(@episteme)
- ベストアンサー率46% (546/1184)
「やってはいけない」ことをやってます。 このときの動作は"未定義"なので、どんなふるまいをしてもそれに理由などありません。 > 上記のようなことをしたい場合、どのように記述するべきでしょうか? CSub child; CParent* pParent = &child;
お礼
ご回答ありがとうございます。 やはりやってはいけないことですよね。これは想定通りでした。 ただ、なぜこのコードでfunc2が正常にコール出来ているのかが分かりません。CSubにキャストしているとはいえ、CParentの実体を指しているポインタ経由でCSubにしかないメンバ関数をなぜ呼び出せるのでしょう?
お礼
なるほど、そういうコードを出力するのですか。直感的には理解しがたいですね。 pSubポインタの指し示す先にあるオブジェクトの中のメンバ関数テーブルにfunc2はないはずだから、コンパイルは通っても実行時エラーになるというのが直感的な感覚です。 ともあれ、このようなコードはたとえ動作しても全く無意味であることが分かりました。 ありがとうございました。