' P '

whatever I will forget

C++ Polymorphism Interfaceとしてabstract classを使う

ルール

  • InterfaceクラスはPure virtual functionしか持てない

  • 上記pure virtual functionは必ずpublicで宣言すること

  • C++にはInterfaceとして定義する構文は無い

  • そのためabstractクラスとpure virtual functionでinterfaceを実装する

使い方

  1. Base classからInterface Classを継承しておくことで、Baseクラスの子クラスからinterface classのfunctionを使うことができる
  2. Interface Classを継承しておけば新しいクラスを作ってもInterface classのfunctionを呼び出すことができる

まあなんでインターフェースがいるの?って疑問に関しては下記参照がいいかも。(Javaでの質問ですが。。)
"未来に備えて新しいクラスで共通的に使えるfunctionを定義しておくため"
みたいな感じでとりあえず消化しましたw

teratail.com

とりあえず例として、objをosに渡すようなoverload operatorがあります。
しかし、このoverload operatorは、それぞれのクラスを引数として指定しるため、下記のような場合だと、 Savingクラスを渡してもAccountクラス側のoverload operatorがcallされてしまいます。

#include <iostream>

class Account {
  friend std::ostream &operator<<(std::ostream &os, const Account &acc);
public:
  virtual void withdraw(double amount) {
    std::cout << "In Account Withdraw method is called" << std::endl;
  }
  virtual ~Account() {}
};

std::ostream &operator<<(std::ostream &os, const Account &acc) {
  os << "Account display";
  return os;
}

class Savings: public Account {
  friend std::ostream &operator<<(std::ostream &os, const Savings &sav);
public:
  virtual void withdraw(double amount) {
    std::cout << "In Savings Withdraw method is called" << std::endl;
  }
  virtual ~Savings() {}
};

std::ostream &operator<<(std::ostream &os, const Savings &sav) {
  os << "Savings display";
  return os;
}

int main (){
  Account a;
  Savings s;
  a.withdraw(1000);
  s.withdraw(1000);

  Account *aptr = new Account();
  Account *sptr = new Savings();
  std::cout << *aptr << std::endl;
  std::cout << *sptr << std::endl;  //This is problematic

  delete aptr;
  delete sptr;
}

実行結果

In Account Withdraw method is called
In Savings Withdraw method is called
Account display
Account display

そこで、objを渡してprintするようなInterface classを作ってしまえば、各classで使えるようになるので、問題は解決するわけです。
気をつけないといけないのは、interface class内で作ったprint functionを各classの中でoverrideすること。かつ
関数宣言を完全にマッチさせること。
度々C++では、Interfaceクラスであることを明示的に示すために、I_***とするようです。

#include <iostream>

class I_Printable {
  friend std::ostream &operator<<(std::ostream &os, const I_Printable &obj);
public:
  virtual void print(std::ostream &os) const = 0; //pure virtual function
};

std::ostream &operator<<(std::ostream &os, const I_Printable &obj){
  obj.print(os);
  return os;
}

class Account: public I_Printable {
public:
  virtual void withdraw(double amount) {
    std::cout << "In Account Withdraw method is called" << std::endl;
  }
  virtual void print(std::ostream &os) const override {
    os << "Account";
  }
  virtual ~Account() {}
};

class Savings: public Account {
public:
  virtual void withdraw(double amount) {
    std::cout << "In Savings Withdraw method is called" << std::endl;
  }
  virtual void print(std::ostream &os) const override {
    os << "Savings";
  }
  virtual ~Savings() {}
};

class client1: public I_Printable {
  virtual void print(std::ostream &os) const override {
    os << "I'm client 1, nice to meet you.";
  }
};

int main (){
  Account a;
  Savings s;
  a.withdraw(1000);
  s.withdraw(1000);

  Account *aptr = new Account();
  Account *sptr = new Savings();
  std::cout << *aptr << std::endl;
  std::cout << *sptr << std::endl;

  client1 *cptr = new client1();
  std::cout << *cptr << std::endl;

  delete aptr;
  delete sptr;
  delete cptr;
}

実行結果

In Account Withdraw method is called
In Savings Withdraw method is called
Account
Savings
I'm client 1, nice to meet you.

処理の流れとしては、クラスオブジェクトのポインタがcoutで呼ばれたら、
Interfaceクラスの<<operatorメソッド内に遷移し、その後各クラス内のprint関数が呼ばれる。
また、強調したいのがclient1というAccountクラスと何の関係の無いクラスを作ったとしても、
Interfaceクラスを継承することによって、print関数が使えていること。