金融 > Implementing_QuantLib日本語訳 > 付録A

Implementing QuantLib日本語訳 付録A


これは、Luigi Ballabio氏が書かれているImplementing QuantLibの翻訳です。氏の許可を得て、翻訳・公開しています。
日本語訳の情報は、こちらを参照してください(日本語訳に関する質問等もここのフォームにお願いします)。
The content of this page is licensed under Creative Commons.



A. 半端物

靴に、船に、封蝋に、キャベツに、王様に

A.7.1 スマートポインタとハンドル

実行時ポリモーフィズムの使用は、大部分ではないが多くのオブジェクトがヒープに割り当てられることを要求します。 これはメモリ管理の問題を起こします。他の言語では組み込みのガベージコレクションによって問題が解決されますが、C++では開発者の責任へと問題が残ります。

私はメモリ管理の多くの問題を長々と語りません。既にすべてがうまくいっている時はそれらは単に困難なものですと言えば十分ですが、例外の可能性が絵に入る時はさらに悪くなるでしょう。 その課題の困難さは、手動の管理を思いとどまらせるのに十分です。しがたって、いくつかの方法は処理を自動化するということに気づかされます。

C++開発者コミュニティでの選択の武器は、スマートポインタであるということに帰着されました。それは組み込みのポインタのようにふるまいますが、まだ必要な間ポイントされたオブジェクトの生存を、そしてもはや必要でない時にデストラクションを気にかけるクラスです。 異なる技術を使ったそのようなクラスのいくつかの実装が存在します。 私たちの選択の一つは、最近TR1技術報告へ含まれ、ANSI/ISO C++標準の次の改正の一部になりそうなBoostライブラリからのスマートポインタ(とくにshared_ptr)です。 私はドキュメントのために読者をBoostサイトへ差し向け、ここではQuantLibでのその使用は完全にメモリ管理を自動化したとだけ述べておきます。 オブジェクトはいたるところで動的に割り当てられます。しかしながら、ライブラリが構成する数万行すべてで一つのdelete文も存在しません。

ポインタへのポインタ(短時間の補習が必要なら、その目的と意味のために次のページの余談を見てください)もまた、スマートな等価物で置き換えられます。 私たちは単にスマートポインタへのスマートポインタを使うことを選びませんでした。一方では、

boost::shered_ptr<boost::shared_ptr<YieldTermStructure>>

と書くことは、Emacsを使ってもすぐに間にうんざりしますし、また一方では、内部のshared_ptrは動的に割り当てられなけれいけないためです。さらに可観測性を実装することが困難になるためです。 かわりに、この目的のためにHandleと呼ばれるクラステンプレートが用意されました。 リストA.8で示されるその実装は、スマートポインタを保持するLinkと呼ばれる中間内部クラスを当てにします。 一方、HandleクラスはLinkインスタンスへのスマートポインタを保持し、それをより簡単に使うことを可能にするメソッドで飾り付けされます。 要求される振る舞いはほとんど無料で手に入ります。なぜなら与えられたハンドルのすべてのコピーは同様のリンクを共有するため、コピーのいずれか一つが新しいオブジェクトへリンクするとき、新しいポインティへのアクセスが全てで与えられます。

  • リストA.8:Handleクラステンプレートの概要
template <class Type>
class Handle {
    protected :
        class Link : public Observable , public Observer {
            public:
                explicit Link( const shared_ptr<Type>& h =
                               shared_ptr<Type>() );
                void linkTo( const shared_ptr<Type>& );
                bool empty() const;
                void update() { notifyObservers(); }
            private :
                shared_ptr<Type> h_;
        };
        boost::shared_ptr<Link<Type> > link_;
    public:
        explicit Handle( const shared_ptr<Type>& h =
                         shared_ptr<Type>() );
        const shared_ptr<Type>& operator−>() const;
        const shared_ptr<Type>& operator *() const;
        bool empty() const ;
        operator boost::shared_ptr<Observable>() const;
};
 
template <class Type>
class RelinkableHandle : public Handle<Type> {
    public:
        explicit RelinkableHandle( const shared_ptr<Type>& h =
                                   shared_ptr<Type>() );
        void linkTo( const boost : : shared_ptr<Type>&) ;
};

また含まれたsharet_ptr<Link>は、他のクラスによって観測されるための手段をハンドルへ与えます。 Linkクラスは観察者であり観察対象です。そのポインティからの通知を受け取り、自身の観察者へその通知を転送します。それと同様に異なるポインティを指し示されるたびに自身の通知を送信します。 単に含まれたリンクを返すshared_ptr<Observable>への自動的変換を定義することで、ハンドルはこの振る舞いの利点を得ます。 したがって、文

registerWith(h)

は、正当で期待したとおりに動作します。登録された観察者は、リンクと(間接的に)指し示されたオブジェクトの両方から通知を受け取ります。

ハンドルを再リンクする方法(つまり、すべてのそのコピーが異なるオブジェクトを指し示すようにすること)が、Handleクラス自体では与えられないことに気づいたかもしれません。しかし派生されたRelinkableHandleクラスで与えられます。 このための原理は、ハンドルが再リンクのために使われる制御を与えることです。 典型的な利用ケースでは、Handleインスタンスはインスタンス化(つまり、イールドカーブを保持するため)され、多くの商品、プライシングエンジンやハンドルのコピーを保持し必要な時に使われる他のオブジェクトへ渡されるでしょう。 論点は、オブジェクト(または、もしオブジェクトが観察者を通じ露出するなら、ハンドルを保持したクライアントコード)は保持したハンドルをいかなる理由があろうと再リンクさせないということです。それをさせてしまうことは、多くの他のオブジェクトへ影響を与えます。 あなたが望むなら、リンクはオリジナルのハンドル、つまりマスターハンドルからのみ変更されるべきです。

人間の弱さを考えると、私たちはこれをコンパイラにより強制させたかったです。 linkToメソッドをconstなものにし、私たちの観察者からconstなハンドルを返すことは上手くいかないでしょう。クライアントコードは非constなハンドルを得るコピーを単純に行えました。 したがって、私たちはHandleインターフェイスからlinkToを削除し、それを派生クラスへ追加しました。 型システムは上手く私たちの利点に働きます。 一方、私たちはRelinkableHandleとしてマスターハンドルをインスタンス化し、Handleを予期しているいかなるオブジェクトへそれを渡すことができます。薄切りになったしかし完全に動作するハンドルのオブジェクトを残しながらも、派生から基底クラスへの自動的変換が起こります。 他方、Handleインスタンスが観察者から戻された時、それをRelinkableHandleへダウンキャストする方法がありません。


余談:ポインタの意味

以下のコードのように、クラスインスタンスでポインタのコピーを保持することは、その所有者がポインティの現在の値にアクセスすることを許可します。

class Foo {
        int* p;
    public:
        Foo( int* p ) : p( p ) {}
        int value() { return *p; }
};
 
int i = 42;
int *p = &i;
Foo f( p );
cout << f.value(); // 42をプリントする
i++;
cout << f.value(); // 43をプリントする

しかしながら、外部のポインタが変更された際、保持されたポインタ(オリジナルのコピー)は変更されません。

int i = 42, j = 0;
int *p = &i;
Foo f( p );
cout << f.value(); // 42をプリントする
p = &j;
cout << f.value(); // まだ42をプリントする

通常、解決法は間接のもう一つの階層を追加することです。Fooをポインタへのポインタを保持するように変更することは、クラスに両方の可能性を与えます。

int i = 42, j = 0;
int *p = &i;
int **pp = &p;
Foo f( pp );
cout << f.value(); // 42をプリントする
i++;
cout << f.value(); // 43をプリントする
p = &j;
cout << f.value (); // 0をプリントする

(以下翻訳中)

最終更新:2010年01月18日 17:46