More C++ Idioms/代数的階層(Algebraic Hierarchy)
出典: フリー教科書『ウィキブックス(Wikibooks)』
目次 |
[編集]
代数的階層(Algebraic Hierarchy)
[編集] 意図
密接に関連した複数の代数的な抽象(数)を単一の汎用的な抽象に隠蔽し、汎用的なインタフェースを提供する。
[編集] 別名
[編集] 動機
Smalltalk のような純粋なオブジェクト指向言語では、変数は、オブジェクトへの実行時の束縛であり、ラベルのように働く。 変数をあるオブジェクトに束縛するということは、オブジェクトにラベルを貼り付けるようなものである。 これらの言語における代入は、ラベルをあるオブジェクトからはがして、別のオブジェクトに貼り付けることに例えられる。 一方、C/C++ では、変数は、オブジェクト対するラベルではなく、アドレスやオフセットの別名である。代入はラベル付けし直すことではなく、古い内容を新しい内容で上書きすることを意味する。代数的階層(Algebraic Hierarchy)イディオムは、C++ でオブジェクトへの束縛を示す弱い変数をシミュレートするため委譲された継承(delegated polymorphism)を用いる。 代数的階層(Algebraic Hierarchy)イディオムはまた、その実装で封筒・便箋(Envelope Letter)イディオムを使用する。 以下のようなコードを書けるようにすることが、このイディオムの背景にある動機である。
Number n1 = Complex (1, 2); // 複素数用のラベル n1 Number n2 = Real (10); // 実数用のラベル n2 Number n3 = n1 + n2; // 加算の結果を n3 としてラベル付け Number n2 = n3; // ラベル付けし直す
[編集] 解法とサンプルコード
以下が、代数的階層(Algebraic Hierarchy)イディオムの実装を示す完全なコードである。
#include <iostream> using namespace std; struct BaseConstructor { BaseConstructor(int=0) {} }; class RealNumber; class Complex; class Number; class Number { friend class RealNumber; friend class Complex; public: Number (); Number & operator = (const Number &n); Number (const Number &n); virtual ~Number(); virtual Number operator + (Number const &n) const; void swap (Number &n) throw (); static Number makeReal (double r); static Number makeComplex (double rpart, double ipart); protected: Number (BaseConstructor); private: void redefine (Number *n); virtual Number complexAdd (Number const &n) const; virtual Number realAdd (Number const &n) const; Number *rep; short referenceCount; }; class Complex : public Number { friend class RealNumber; friend class Number; Complex (double d, double e); Complex (const Complex &c); virtual ~Complex (); virtual Number operator + (Number const &n) const; virtual Number realAdd (Number const &n) const; virtual Number complexAdd (Number const &n) const; double rpart, ipart; }; class RealNumber : public Number { friend class Complex; friend class Number; RealNumber (double r); RealNumber (const RealNumber &r); virtual ~RealNumber (); virtual Number operator + (Number const &n) const; virtual Number realAdd (Number const &n) const; virtual Number complexAdd (Number const &n) const; double val; }; /// (封筒・便箋(Envelope Letter)イディオムにおける)便箋(letters)によってのみ使用される Number::Number (BaseConstructor) : rep (0), referenceCount (1) {} /// ユーザと静的ファクトリ関数によって使用される Number::Number () : rep (0), referenceCount (0) {} /// ユーザと静的ファクトリ関数によって使用される Number::Number (const Number &n) : rep (n.rep), referenceCount (0) { cout << "Number::Number による Number の生成\n"; if (n.rep) n.rep->referenceCount++; } Number Number::makeReal (double r) { Number n; n.redefine (new RealNumber (r)); return n; } Number Number::makeComplex (double rpart, double ipart) { Number n; n.redefine (new Complex (rpart, ipart)); return n; } Number::~Number() { if (rep && --rep->referenceCount == 0) delete rep; } Number & Number::operator = (const Number &n) { cout << "Number::operator= による Number の代入\n"; Number temp (n); this->swap (temp); return *this; } void Number::swap (Number &n) throw () { std::swap (this->rep, n.rep); } Number Number::operator + (Number const &n) const { return rep->operator + (n); } Number Number::complexAdd (Number const &n) const { return rep->complexAdd (n); } Number Number::realAdd (Number const &n) const { return rep->realAdd (n); } void Number::redefine (Number *n) { if (rep && --rep->referenceCount == 0) delete rep; rep = n; } Complex::Complex (double d, double e) : Number (BaseConstructor()), rpart (d), ipart (e) { cout << "Complex の生成\n"; } Complex::Complex (const Complex &c) : Number (BaseConstructor()), rpart (c.rpart), ipart (c.ipart) { cout << "Complex::Complex による Complex の生成\n"; } Complex::~Complex() { cout << "Complex::~Complex() 内部\n"; } Number Complex::operator + (Number const &n) const { return n.complexAdd (*this); } Number Complex::realAdd (Number const &n) const { cout << "Complex::realAdd\n"; RealNumber const *rn = dynamic_cast <RealNumber const *> (&n); return Number::makeComplex (this->rpart + rn->val, this->ipart); } Number Complex::complexAdd (Number const &n) const { cout << "Complex::complexAdd\n"; Complex const *cn = dynamic_cast <Complex const *> (&n); return Number::makeComplex (this->rpart + cn->rpart, this->ipart + cn->ipart); } RealNumber::RealNumber (double r) : Number (BaseConstructor()), val (r) { cout << "RealNumber の生成\n"; } RealNumber::RealNumber (const RealNumber &r) : Number (BaseConstructor()), val (r.val) { cout << "RealNumber::RealNumber による RealNumber の生成\n"; } RealNumber::~RealNumber() { cout << "RealNumber::~RealNumber() 内部\n"; } Number RealNumber::operator + (Number const &n) const { return n.realAdd (*this); } Number RealNumber::realAdd (Number const &n) const { cout << "RealNumber::realAdd\n"; RealNumber const *rn = dynamic_cast <RealNumber const *> (&n); return Number::makeReal (this->val + rn->val); } Number RealNumber::complexAdd (Number const &n) const { cout << "RealNumber::complexAdd\n"; Complex const *cn = dynamic_cast <Complex const *> (&n); return Number::makeComplex (this->val + cn->rpart, cn->ipart); } namespace std { template <> void swap (Number & n1, Number & n2) { n1.swap (n2); } } int main (void) { Number n1 = Number::makeComplex (1, 2); Number n2 = Number::makeReal (10); Number n3 = n1 + n2; cout << "終了\n"; return 0; }
[編集] 既知の利用
[編集] 関連するイディオム
[編集] References
Advanced C++ Programming Styles and Idioms by James Coplien, Addison Wesley, 1992.