2017年8月3日 星期四

虛擬函數多層強制實作

這裡留下 http://www.programmer-club.com.tw/ShowSameTitleN/c/46861.html 的討論結果。

C++ 的純虛擬函數只能強制實作一層,若希望多層的實作類別都必須實作,C++ 並沒有支援,以下示範如何建立多層強制實作的機制


#include <iostream>
#include <sstream>
#include <memory>

using namespace std;

class A;
class B;
class C;
class D;

template <typename classification>
class Prototype_CRTP;

class Prototype_ROOT;

class Visitor
{
public:
  Visitor(){}
  ~Visitor(){}

  void Visit(A *a);
  void Visit(B *b);
  void Visit(C *c);
  void Visit(D *d);
};

class Prototype
{
  // Constructor 放這裡為了不被直接繼承
  Prototype() {}
public:
  virtual ~Prototype() {}

  // 永續儲存
  virtual void Save(ostream &save) = 0;
  virtual void Load(istream &load) = 0;

  // 接收訪問者物件,將自己傳給訪問者
  virtual void Accept(Visitor *v) = 0;

  // 取得和實體物件相同的物件
  virtual Prototype *Clone() = 0;
  
  friend class Prototype_ROOT;
};

// Prototype 延伸類別要繼承的最上層類別
class Prototype_ROOT
{
  // 給 class Prototype_CRTP<> 用的
  // 以取得繼承 Prototype 的權力
  class Prototype_R :public Prototype
  {
  public:
    Prototype_R() {}
    ~Prototype_R() {}
  };

public:

  Prototype_ROOT() {}
  ~Prototype_ROOT() {}

  template <typename classification>
  friend class Prototype_CRTP;
};

// 為了可以強制實作及多重繼承
template <typename classification>
class Prototype_CRTP : virtual public Prototype_ROOT::Prototype_R
{
protected:
  // 永續儲存
  virtual void Save(ostream &save) override = 0;
  virtual void Load(istream &load) override = 0;
  // 接收訪問者物件,將自己傳給訪問者
  virtual void Accept(Visitor *v) override = 0;
  // 取得和實體物件相同的物件
  virtual Prototype *Clone() override = 0;

public:
  Prototype_CRTP()  {}
  ~Prototype_CRTP(){}
};

#define PROTOTYPE_BASE_CRTP(Base) Base,public Prototype_CRTP<Base>

// 單層繼承
class A :public PROTOTYPE_BASE_CRTP(Prototype_ROOT)
{
  // 接收訪問者物件,將自己傳給訪問者
  virtual void Accept(Visitor *v) override
  {
    v->Visit(this);
  }

  // 取得和實體物件相同的物件
  virtual Prototype *Clone() override
  {
    return new A(*this);
  }

  // 身高
  int m_Height;

protected:
  // 永續儲存
  virtual void Save(ostream &save) override
  {
    save << m_Height << ' ';
  }
  virtual void Load(istream &load) override
  {
    load >> m_Height;
  }

public:
  A(int Height) 
  {
    m_Height = Height;
  }
  A(const A &Src)
  {
    m_Height = Src.m_Height;
  }
  virtual ~A() {}

  int GetHeight() const
  {
    return m_Height;
  }
};

// 單層繼承
class B :public PROTOTYPE_BASE_CRTP(Prototype_ROOT)
{
  // 接收訪問者物件,將自己傳給訪問者
  virtual void Accept(Visitor *v) override
  {
    v->Visit(this);
  }

  // 取得和實體物件相同的物件
  virtual Prototype *Clone() override
  {
    return new B(*this);
  }

  // 身高
  int m_Weight;

protected:
  // 永續儲存
  virtual void Save(ostream &save) override
  {
    save << m_Weight << ' ';
  }
  virtual void Load(istream &load) override
  {
    load >> m_Weight;
  }

public:
  B(int Weight)
  {
    m_Weight = Weight;
  }
  B(const B &Src)
  {
    m_Weight = Src.m_Weight;
  }
  virtual ~B() {}

  int GetWeight() const
  {
    return m_Weight;
  }
};

// 多層單一繼承
class C :public PROTOTYPE_BASE_CRTP(A) 
{
  // 接收訪問者物件,將自己傳給訪問者
  virtual void Accept(Visitor *v) override
  {
    v->Visit(this);
  }

  // 取得和實體物件相同的物件
  virtual Prototype *Clone() override
  {
    return new C(*this);
  }

  // 年紀
  int m_Age;

protected:
  // 永續儲存
  virtual void Save(ostream &save) override
  {
    A::Save(save);
    save << m_Age << ' ';
  }
  virtual void Load(istream &load) override
  {
    A::Load(load);
    load >> m_Age;
  }

public:
  C(int Age, int Height)
    :A(Height)
  {
    m_Age = Age;
  }
  C(const C &Src)
    :A(Src)
  {
    m_Age = Src.m_Age;
  }
  virtual ~C() {}

  int GetAge() const
  {
    return m_Age;
  }
};

// 多重繼承
class D :public PROTOTYPE_BASE_CRTP(A), public PROTOTYPE_BASE_CRTP(B)
{
  // 接收訪問者物件,將自己傳給訪問者
  virtual void Accept(Visitor *v) override
  {
    v->Visit(this);
  }

  // 取得和實體物件相同的物件
  virtual Prototype *Clone() override
  {
    return new D(*this);
  }

  // 年紀
  int m_Age;

protected:
  // 永續儲存
  virtual void Save(ostream &save) override
  {
    A::Save(save);
    B::Save(save);
    save << m_Age << ' ';
  }
  virtual void Load(istream &load) override
  {
    A::Load(load);
    B::Load(load);
    load >> m_Age;
  }

public:
  D(int Age, int Height, int Weight)
    :A(Height), B(Weight)
  {
    m_Age = Age;
  }
  D(const D &Src)
    :A(Src), B(Src)
  {
    m_Age = Src.m_Age;
  }
  virtual ~D() {}

  int GetAge() const
  {
    return m_Age;
  }
};


void Visitor::Visit(A *a)
{
  cout << "我是 class A\n"
    << "身高 " << a->GetHeight() << endl << endl;
}

void Visitor::Visit(B *b)
{
  cout << "我是 class B\n"
    << "體重 " << b->GetWeight() << endl << endl;
}

void Visitor::Visit(C *c)
{
  cout << "我是 class C\n"
    << "年紀 " << c->GetAge() << '\n'
    << "身高 " << c->GetHeight() << endl << endl;
}

void Visitor::Visit(D *d)
{
  cout << "我是 class D\n"
    << "年紀 " << d->GetAge() << '\n'
    << "身高 " << d->GetHeight() << '\n'
    << "體重 " << d->GetWeight() << endl << endl;
}

int main()
{
  Visitor v;

  unique_ptr<Prototype> A_Obj(new A(180));
  unique_ptr<Prototype> B_Obj(new B(100));
  unique_ptr<Prototype> C_Obj(new C(50,170));
  unique_ptr<Prototype> D_Obj(new D(40, 175, 98));

  A_Obj->Accept(&v);
  B_Obj->Accept(&v);  
  C_Obj->Accept(&v);
  D_Obj->Accept(&v);

  unique_ptr<Prototype> D_Obj2( D_Obj->Clone() );
  cout << "複製人..." << endl;
  D_Obj2->Accept(&v);

  unique_ptr<Prototype> Big_Obj(new D(2000, 5000, 3000));
  stringstream persistent;
  Big_Obj->Save(persistent);
  D_Obj2->Load(persistent);
  cout << "偷天換日..." << endl;
  D_Obj2->Accept(&v);

  return 0;
}



▲執行結果


接下來再嘗試延伸出一個也含有須要強制實作的介面,來試試這個機制的能耐,這個 Prototype2 要從上個例子的 class D 繼承,別問為什麼要這樣做,反正 C++ 可以這樣做,當作是一種考驗吧。

因 class D 建構時須提供參數,所以其延伸類別也必須提供參數,原本想盡辦法不想讓介面被直接繼承的企圖落空了,因此沒法強制必須使用這個機制只能用勸導,所以 C++ 要是能提供 must 才是根本解決之道,以下只貼上增修的部份。

class Prototype2 : public PROTOTYPE_BASE_CRTP(D)
{
  // 接收訪問者物件,將自己傳給訪問者
  virtual void Accept(Visitor *v) override = 0;

  // 取得和實體物件相同的物件
  virtual Prototype *Clone() override = 0;

  // Constructor 放這裡為了不被直接繼承
  Prototype2(int Age, int Height, int Weight)
    :D(Age, Height, Weight)
  {}

  Prototype2(const Prototype2 &Src)
    :D(Src)
  {}

protected:
  // 永續儲存
  virtual void Save(ostream &save) override
  {
    D::Save(save);
  }
  virtual void Load(istream &load) override
  {
    D::Load(load);
  }

public:
  virtual ~Prototype2() {}

  // 取得實作物件的 this 指標
  virtual void *Get_this() = 0;

  friend class Prototype2_ROOT;
};

// Prototype2 延伸類別要繼承的最上層類別
class Prototype2_ROOT
{
protected:
  // 給 class Prototype2_CRTP<> 用的
  // 以取得繼承 Prototype2 的權力
  class Prototype_R :public Prototype2
  {
  public:
    Prototype_R(const Prototype_R &Src)
      :Prototype2(Src)
    {}
    Prototype_R(int Age, int Height, int Weight)
      :Prototype2(Age, Height, Weight)
    {}

    ~Prototype_R() {}
  };

public:

  Prototype2_ROOT() {}
  ~Prototype2_ROOT() {}

  template <typename classification>
  friend class Prototype2_CRTP;
};

// 為了可以強制實作及多重繼承
template <typename classification>
class Prototype2_CRTP : virtual public Prototype2_ROOT::Prototype_R
{
protected:
  // 取得實作物件的 this 指標
  virtual void *Get_this() override = 0;

public:

  Prototype2_CRTP() {}
  ~Prototype2_CRTP() {}
};

#define PROTOTYPE2_BASE_CRTP(Base) Base,public Prototype_CRTP<Base>,public Prototype2_CRTP<Base>

// 單層繼承
class E :public PROTOTYPE2_BASE_CRTP(Prototype2_ROOT)
{
  // 接收訪問者物件,將自己傳給訪問者
  virtual void Accept(Visitor *v) override
  {
    v->Visit(this);
  }

  // 取得和實體物件相同的物件
  virtual Prototype *Clone() override
  {
    return new E(*this);
  }

protected:
  // 永續儲存
  virtual void Save(ostream &save) override
  {
    Prototype2::Save(save);
  }
  virtual void Load(istream &load) override
  {
    Prototype2::Load(load);
  }

public:
  E(int Age, int Height, int Weight)
    : Prototype2_ROOT::Prototype_R(Age, Height, Weight)
  {}
  E(const E &Src)
    :Prototype2_ROOT::Prototype_R(Src)
  {}
  virtual ~E() {}

  // 取得實作物件的 this 指標
  virtual void *Get_this() override
  {
    return this;
  }
};

void Visitor::Visit(E *e)
{
  cout << "我是 class E\n"
    << "年紀 " << e->GetAge() << '\n'
    << "身高 " << e->GetHeight() << '\n'
    << "體重 " << e->GetWeight() << '\n'
    << "駐在 " << e->Get_this() << endl << endl;
}

int main()
{
  Visitor v;

  unique_ptr<Prototype> A_Obj(new A(180));
  unique_ptr<Prototype> B_Obj(new B(100));
  unique_ptr<Prototype> C_Obj(new C(50,170));
  unique_ptr<Prototype> D_Obj(new D(40, 175, 98));
  unique_ptr<Prototype> E_Obj(new E(111, 222, 333));

  A_Obj->Accept(&v);
  B_Obj->Accept(&v);
  C_Obj->Accept(&v);
  D_Obj->Accept(&v);
  E_Obj->Accept(&v);

  unique_ptr<Prototype> D_Obj2( D_Obj->Clone() );
  cout << "複製人..." << endl;
  D_Obj2->Accept(&v);

  unique_ptr<Prototype> Big_Obj(new D(2000, 5000, 3000));
  stringstream persistent;
  Big_Obj->Save(persistent);
  D_Obj2->Load(persistent);
  cout << "偷天換日..." << endl;
  D_Obj2->Accept(&v);

  return 0;
}

▲執行結果


範例程式檔按這下載






沒有留言:

張貼留言