C++ での Mixin の活用 : Comparable を使って比較演算子を簡単実装
オブジェクト指向プログラミングには Mixin という手法があります。 これを使えば、自作のクラスを比較可能な(Comparable)クラスにすることが簡単に出来ます。 今回は Comparable Mixin を例に C++ での Mixin のやり方について説明します。
Mixin とインターフェース
Mixin の前提として、 多重継承の問題点を把握しておく必要があります。 その辺の話や Mixin の概要に関しては以下の記事をみていただくとして、 C++ に限定して簡単に説明していきます。 多重継承の問題を避けるための代表的な方法はインターフェースです。class IComparable { public: virtual int Compare(const IComparable &other) const = 0; };インターフェースの制限は次の 2 つです。 この制限によりインタフェースクラスは多重継承しても問題が発生しないので、クラスに対して自由に継承させられます。
- 属性(メンバー変数)を持たない
- 操作(メンバー関数)は宣言のみで実装を持たない(純粋仮想関数)
- 利用側 : 操作(Compare) を持つことが保証される
- 継承側 : 操作(Compare) の実装が強制される
インターフェースはインターフェースで有用です。 ただ、 多重継承で問題になるのは属性を持つことの方で、 操作の実装を禁止する必要はありません。
「操作の実装を持つ」ということは言い換えるとなんらかの「機能を持つ」ということです。 そういったクラスを継承すると元のクラスに機能を付加することになります。 継承によって機能を追加するためのクラスやその手法を Mixinと呼びます。
例えば、『鯨』というクラスを考えてみます。
ほ乳類、魚類から継承したとするとダイヤモンド継承(ひし形継承)と呼ばれる共通の祖先を持つクラスの継承となり、多重継承で最も問題になる形になります。
Mixin を用いると「泳げる」(Swimable) という機能を共に持つとみなすことができます。 ダイヤモンド継承は避けられますし、こちらの方がより自然にクラスによるモデル化ができています。
Comparable Mixin
Mixin をさらに Comparable(比較できる) Mixin を具体例として説明していきます。次のような点クラスについて考えてみます。
ちなみに struct はデフォルトが public なことを除いて class と同じものです。
struct Point { int x; int y; };点クラスのように 等比、比較演算子のオーバーロードを行うと使いやすくなるクラスは多いと思いますが、 毎回それらを実装するのは面倒です。
しかし、 それらの演算子は比較関数から導くことが出来ます。 これを実装したクラスが Comparable Mixin のクラスです。
template <class T> class ComparableMixIn { public: /// 比較用関数 /// /// | 状態 | 戻り値 | /// |--------------------|----------| /// | other の方が大きい | 負の値 | /// | other と一致 | 0 | /// | other の方が小さい | 正の値 | /// virtual int Compare(const T &other) const = 0; // 演算子の実装 bool operator==(const T &other) const { return Compare(other) == 0; }; bool operator!=(const T &other) const { return Compare(other) != 0; }; bool operator< (const T &other) const { return Compare(other) < 0; }; bool operator<=(const T &other) const { return Compare(other) <= 0; }; bool operator> (const T &other) const { return Compare(other) > 0; }; bool operator>=(const T &other) const { return Compare(other) >= 0; }; };Mixin クラスの特徴は次のようなものです。 インターフェースと同様に属性を持たないので多重継承ができますが、 機能(操作の実装)を持っているところが違います。
- 属性を持たない
- キーとなる関数の宣言(純粋仮想関数)
- キー関数を使って機能となる関数を実装
一方、使う側である点クラスでは ComparableMixIn を継承し、 Compare() を実装します。
ここには実装のちょっとしたテクニックがあります。 ComparableMixIn は継承先の型をとるテンプレートです。こうすることによって継承先の型による演算子のオーバーロードを可能にしています。
struct Point : public ComparableMixIn<Point> { : /// 比較関数. /// x で比較し、同じなら y で比較 virtual int Compare(const Point &other) const override { return ((x != other.x) ? (x - other.x) : (y - other.y)); } };
この Mixin によって、 点クラスは比較の機能をもつことができるようになります。
Point a(1, 2), b(2, 3); cout << boolalpha; cout << "a == b : " << (a == b) << endl; cout << "a != b : " << (a != b) << endl; cout << "a < b : " << (a < b) << endl; cout << "a <= b : " << (a <= b) << endl; cout << "a > b : " << (a > b) << endl; cout << "a >= b : " << (a >= b) << endl;実行結果 :
a == b : false a != b : true a < b : true a <= b : true a > b : false a >= b : falseなお、Boost::Operators を使うと == から != 、 < から他の不等号を定義できます。 厳密に言うと < さえ定義されていれば、 すべての等、不等号 を導くことができます。 (a == b は a < b && b < a)
これは < 演算子をキー関数とした Mixin ということもできます。
ソースファイル
以上のように Comparable Mixin を使うと Compare() を実装するだけで、等号、不等号の機能を簡単に追加することができます。大したコード量ではありませんが、ヘッダーを公開していますので、ダウンロード(リンク先を保存)して自由に使ってください。[MIT ライセンス]
- ヘッダー : ComparableMixIn.h
- 使用例 : use_mixin.cpp
- 関連記事
-
- C++14 Streams を使った関数型のデータ処理
- C++11 範囲に基づく for 文
- C++ での Mixin の活用 : Comparable を使って比較演算子を簡単実装
- C++ のスタイルを変えるかもしれない右辺値参照とムーブセマンティクス
- C++ typename の 2 つの使い方
Facebook コメント
コメント