Skip to content.

Sections
Personal tools
You are here: Home » コミュニティ » masarl memorial » homepage3.nifty.com » masarl » article » nifty-logs » Niftyの過去ログ集 - クロスキャストと Capsule パターン

Niftyの過去ログ集 - クロスキャストと Capsule パターン

Document Actions

クロスキャストと Capsule パターン

親クラスから子クラスへキャストするのがダウンキャスト,子クラスから親クラスへキャストするのがアップキャスト.ここまではいいですね.

さて,これ以外に別のキャストがある,といったら驚くでしょうか? クロスキャストはキャストの一つですが,あるクラスからまったく別のクラスへキャストするという,ちょっと常識に反するキャストです.あるいは,父親クラスから母親クラスへキャストする,といった方がいいかもしれません.

02629/02629 BYI20012  まさーる         RE^19:C++が絶対というわけではない
( 6)   99/02/07 01:31  02626へのコメント

へげもん さん、こんにちは。

>>Capsuleパターンは,dynamic_castでクラス階層の兄弟にキャストするという技
>>(Cross Cast)を使ったものなんで..(子供じゃありません)
>
> うーん。でも兄弟クラスが持っているメソッドや属性を自分が持っていると
>は限らないので(そもそも、持っていたらキャストは要らない)、その場合は
>常に失敗すると思うんですが。うまくいきそうなのは、やはりダイアモンド継
>承して、
>            先祖
>          /    \
>        父        母
>          \    /
>             子
>
>実体が「子」の時に、それを指している「父」ポインタを「母」ポインタへキ
>ャストする場合だと思います。

おしいですが,上で先祖がいる必要はないんです(兄弟という言い方はまずかっ
たですね,すいません).

        父        母
          \    /
             子

実体が子のとき,子を参照している父ポインタから母ポインタへのキャストは成
功しますから.だから少なくとも多重継承が必ずないとだめですね.
(注意すべきは,お父さんとお母さんがもともと他人だ,ということです)

C++で書くとこうなります:

class 父 {};
class 母 {};
class 子 : public 父, public 母 {};

父* ちち = new 子;
母* はは = dynamic_cast<母*>(ちち); // これはOK.

この上のコードで,子の出ている行を省いて

class 父 {};
class 母 {};
母* はは = dynamic_cast<母*>(ちち); // これはOK.

だけみるとちょっとびっくりするでしょ^^;.

> で、これってどんな場合に役立つんでしょうね?(^^;
> COMのインターフェイスで横飛びするとか?

Capsuleパターンを一言でいえば,cross castを使った安全なvoid*を実現する方
法です.このパターンでは,void*の代わりにCapsuleクラスを作ります:

class Capsule { public: virtual ~Capsule(){} }; 

それで,例えば関数のコールスタックの奥深くで次のようなクラス

class A : public Capsule, public InterfaceA, public InterfaceB {};

のインスタンスを返す関数

Capsule* bottomFunc() { return new A(); }

があって,中間の関数はどんどん上のレベルの関数にCapsuleポインタを渡して
いくだけ(しかもそいつらがInterfaceA, InterfaceBを持っているとは全く感知
せず),最後に上位レベルの関数が

Capsule* c = f();
if (InterfaceA* a = dynamic_cast(c)) { ... }
if (InterfaceB* b = dynamic_cast(c)) { ... }

と処理する,というようなパターンです.

重要なのは,IntarfaceAとInterfaceBは共通の親を持ってないし,Capsuleのこ
とは何もしらない.途中の関数はCapsule以外なにも知らない.うーん,依存関
係がだいぶすっきりした,うれしーということです.

普通は,void* を共通の親クラスに見立てて,

class InterfaceA : public Capsule {};
class InterfaceB : public Capsule {};
class A : public InterfaceA, public InterfaceB {};

とやってしまうでしょう.そもそもダイアモンドなのがいやなのと,余計な依存
関係が増えてしまっていやですね.いずれにせよこの方法でもdynamic_castはい
りますし.

Martinの挙げている例はthrow/catchができないシビアな環境でのエラー処理で
す(でもdynamic_castぐらいは見逃してねっていう環境^^;).

ちょっと思い付くところでは,ObserverパターンでObserverにObservableの更新
情報を渡す引数にCapsuleパターンを使うとか,Chain of Responsibilityパター
ンで次のオブジェクトに渡すときの引数としてつかうとかいろいろできそうです.

>>#C++を使わなくなってもうすぐ3年..もうほとんど忘れました(泣).
>
> え、じゃあ今は何をお使いで?

そういうの使うような仕事をもうしてないですから(T_T).

                                     99/02/07(日) まさーる(BYI20012)

Capsule パターンは,Robert C. Martin によるものです.いつか使おうとたくらんでいるのですがまだ実現できてません.

参考資料