2014年4月10日木曜日

stackoverflow: std::enable_shared_from_thisとそれを継承したクラスAとそのAを継承したクラスBについて、BのオブジェクトがAとBそれぞれのメンバー関数を呼んだ際にthisが異なるアドレスを示すのはなんで?


stackoverflowにはいつもお世話になっています。
質問から数分と待たずにそれらしい良い回答を頂けました(╹◡╹)
しかも簡単ながらメモリーレイアウトの図解も入れてくれてとってもわかりやすい!

[質問]

std::enable_shared_from_thisとそれを継承したクラスAとそのAを継承したクラスBについて、BのオブジェクトがAとBそれぞれのメンバー関数を呼んだ際にthisが異なるアドレスを示すのはなんで?

[答え(おおよそ意訳)]

これはstd::enable_shared_from_thisを使っているからという事ではなくて、多重継承によって派生したクラスの持つオブジェクトのメモリーレイアウトはどのように配置されるか、そしてその時のthis、つまりある型Xに対してのX*はどのように解決されるか、という問題なんだ。

例のコードでは B の基底クラスを B: S<B>, A と定義しているから、メモリーレイアウトとしては、

+-------+-----+---------------------------+
| S<B>  | A   | B's non-inherited members |
+-------+-----+---------------------------+
こうなったんだよ。

Bのオブジェクトは、S<B>のオブジェクトを最初に配置して、次にA、そしてBそのものの継承していないメンバーを配置されたんだ。

そして、AオブジェクトはBの一部に存在していて、そのメンバー関数のthisは自身の型はA型で、そのA型のオブジェクトの入っているアドレスをA*をthisとして扱うし、S<B>も同様にS<B>型のオブジェクトの入っているアドレスS<B>*をthisとして扱う。今回はこのレイアウトの為にオブジェクトBにおけるBそのものとS<B>のアドレスは同じで、AのアドレスがBの一部でAの領域のBのアドレスとは異なるアドレスになったというわけ。

[ちなみに]

なるほどー!

と、いうわけで、つまり今回のサンプルのような例の場合に、faでもfbでもthisで同じアドレスを返して欲しい場合には、B: A, S<B> と派生クラスを定義してあげるとBオブジェクトのB::fb()もA::fa()もthisで同じアドレスを示すようになります。


さらに派生するときも同様に、

こうすれば良いのですね(╹◡╹)

0 件のコメント:

コメントを投稿