March 14, 2008operator=()と継承[Computer]
拙作のC++行列ライブラリmatrix.hのメンテナンスを久しぶりにやってみているのですが、部分行列の代入の解釈で少し考えることがありました。簡易的なコードで書くと以下のような問題です。 Matrix m1, m2;
m.partialMatrix = m2; このとき部分行列への代入は、元の行列の書き換えもおこなうべき(上の例でいうとm1も変更されて然るべき)であると思います。現在の実装では行列を構成する要素データ、そして行列という箱、これら2つを別々に管理するフライウェイトパターンを用いていました。そのため代入については要素ごとの代入を行わず中身全体をつけかえる動作をしており、上記のような元の行列まで書き換えるということを想定していませんでした。 そこで部分行列を適当な子クラスで表現し、子クラスで代入演算子のオーバーロードを行うことによって、上記の問題の解決をはかろうとしました。つまり部分行列への代入は要素ごとの代入を行う一方、それ以外の行列では要素をまとめて付け替えるだけというフライウェイトパターンの共存を目指しました。 前置きがながくなってしまいましたが、ここで表題にあるとおりの代入演算子の問題に引っかかりました。どうも代入演算子を継承先でオーバーロードした際、そのオーバーライド(言葉がややこしいですね、笑)が通常の関数と異なるようなのです。違いを明らかにするため、検証用のプログラムoperator_equal_test.cppを書いてみました。要約をすると問題が生じるのは以下のようなケースでした。 class A {
public: A(){} virtual ~A() {} self_t &operator=(const A &a){ cout << "A::operator=(A)"; return *this; } }; class B : public A { int main(){ cout << "a = a => "; a = a; cout << endl; // (1) return 0; さて結果はどうなるでしょうか。(1)、(2)ではAが左辺オペランド、(3)、(4)ではBが左辺オペランドなので通常の関数と同様に考えるのであれば、(1)、(2)ではA::operator=(A)が、(3)、(4)ではB::operator=(A)が呼び出されそうなものです。しかし(4)では意表をついてA::operator=(A)が呼ばれます。VC2005/2008 Express Edition、gcc 3.4.4で試して同一の結果がでたので、おそらくC++の仕様でないかと思います(現在調査中ですが、ご存知でしたら是非お教えください)。なお(4)のケースでA::operator=(A)を呼ばれないようにするためには、B::operator=(B)なる関数を定義する必要がありました。もし、さらに深い継承関係がある場合には、親クラス::operator=(親クラス)で子クラス=子クラスを捕捉することも可能です。 以上のような検証の末、できあがったのが行列ライブラリmatrix.h(1.28)です。まだまだ改良の余地がありそうですが、よろしければ使ってみてください。 コメント
どうもご無沙汰です。ベンチプレスが挙がらなくなったホークスファンです。(わかるかな) さて本題ですが、これはシンプルに B::operator=(B) が自動で生成されて、そのなかで A::operator=(A) が呼ばれているのではないかと思いました。 では仕事にもどるとします。 Posted by: soleus : April 11, 2008 03:18 PM>soleusさん、改めTさん 代入演算子のオーバーロードについて調べていて辿り着きました。 以上、気になりましたのでコメントしました。 Posted by: 通りすがり : February 24, 2009 12:22 PM>通りすがりさん こちらにアップされているサンプルコードを見てみました。 >通りすがりさん なるほど。 もうお気付きのようですが、 サンプルコードでは、たまたまA::operator=(A)のみオーバーロードしていたため、こちらに気付いただけで、fenrirさんの感覚が特にずれているわけではありません。B::operator=(B)もちゃんと呼ばれています^^ ちなみにB::operator=(A)は自動で作られないので、自分で定義しない限り呼ばれません。 少々くどい説明になってしまい申し訳ありません(汗) コメントする
|
スポンサード リンク
|