跳轉到內容

更多 C++ 習語/代數層次結構

來自華夏公益教科書,開放的書籍,面向開放的世界

代數層次結構

[編輯 | 編輯原始碼]

將多個緊密相關的代數抽象(數字)隱藏在一個通用的抽象後面,並提供一個通用的介面。

也稱為

[編輯 | 編輯原始碼]
  • 狀態(Gamma 等人)

在像 Smalltalk 這樣的純面嚮物件語言中,變數是對充當標籤的物件的執行時繫結。將變數繫結到物件就像在物件上貼上標籤。這些語言中的賦值類似於從一個物件上剝下標籤,然後將它貼到另一個物件上。另一方面,在 C 和 C++ 中,變數是地址或偏移量的同義詞,而不是物件的標籤。賦值並不意味著重新標記,它意味著用新內容覆蓋舊內容。代數層次結構習語使用委託多型來模擬 C++ 中的弱變數到物件繫結。代數層次結構在其實現中使用了信封信函習語。這個習語背後的動機是能夠編寫像這樣的程式碼

Number n1 = Complex (1, 2);  // Label n1 for a complex number.
Number n2 = Real (10);  // Label n2 for a real number.
Number n3 = n1 + n2;  // Result of addition is labelled n3.
Number n2 = n3;  // Re-labelling.

解決方案和示例程式碼

[編輯 | 編輯原始碼]

顯示代數層次結構習語實現的完整程式碼

#include <iostream>

struct BaseConstructor { BaseConstructor(int=0) {} };

class RealNumber;
class Complex;
class Number;

class Number
{
    friend class RealNumber;
    friend class Complex;

  public:
    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 ();
    Number (BaseConstructor);

  private:
    void redefine (Number *n);
    virtual Number complexAdd (Complex const &n) const;
    virtual Number realAdd (RealNumber 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 (RealNumber const &n) const;
  virtual Number complexAdd (Complex 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 (RealNumber const &n) const;
  virtual Number complexAdd (Complex const &n) const;

  double val;
};

/// Used only by the letters.
Number::Number (BaseConstructor)
: rep (0),
  referenceCount (1)
{}

/// Used by static factory functions.
Number::Number ()
  : rep (0),
    referenceCount (0)
{}

/// Used by user and static factory functions.
Number::Number (const Number &n)
: rep (n.rep),
  referenceCount (0)
{
  std::cout << "Constructing a Number using Number::Number" << std::endl;
  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)
{
  std::cout << "Assigning a Number using Number::operator=" << std::endl;
  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 (Complex const &n) const
{
  return rep->complexAdd (n);
}

Number Number::realAdd (RealNumber 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)
{
  std::cout << "Constructing a Complex" << std::endl;
}

Complex::Complex (const Complex &c)
  : Number (BaseConstructor()),
    rpart (c.rpart),
    ipart (c.ipart)
{
  std::cout << "Constructing a Complex using Complex::Complex" << std::endl;
}

Complex::~Complex()
{
  std::cout << "Inside Complex::~Complex()" << std::endl;
}

Number Complex::operator + (Number const &n) const
{
  return n.complexAdd (*this);
}

Number Complex::realAdd (RealNumber const &n) const
{
  std::cout << "Complex::realAdd" << std::endl;
  return Number::makeComplex (this->rpart + n.val,
                              this->ipart);
}

Number Complex::complexAdd (Complex const &n) const
{
  std::cout << "Complex::complexAdd" << std::endl;
  return Number::makeComplex (this->rpart + n.rpart,
                              this->ipart + n.ipart);
}

RealNumber::RealNumber (double r)
  : Number (BaseConstructor()),
    val (r)
{
  std::cout << "Constructing a RealNumber" << std::endl;
}

RealNumber::RealNumber (const RealNumber &r)
  : Number (BaseConstructor()),
    val (r.val)
{
  std::cout << "Constructing a RealNumber using RealNumber::RealNumber" << std::endl;
}

RealNumber::~RealNumber()
{
  std::cout << "Inside RealNumber::~RealNumber()" << std::endl;
}

Number RealNumber::operator + (Number const &n) const
{
  return n.realAdd (*this);
}

Number RealNumber::realAdd (RealNumber const &n) const
{
  std::cout << "RealNumber::realAdd" << std::endl;
  return Number::makeReal (this->val + n.val);
}

Number RealNumber::complexAdd (Complex const &n) const
{
  std::cout << "RealNumber::complexAdd" << std::endl;
  return Number::makeComplex (this->val + n.rpart, n.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;

  std::cout << "Finished" << std::endl;

  return 0;
}

已知用法

[編輯 | 編輯原始碼]
[編輯 | 編輯原始碼]

參考文獻

[編輯 | 編輯原始碼]
  • Coplien,James(1992 年)。高階 C++ 程式設計風格和習語Addison-Wesley
華夏公益教科書