抽象クラスと純粋仮想関数

抽象クラスは、将来におけるより具体的な派生クラスを作成する一般的なエンティティを作成する為に作られています。抽象クラスは、いくつかの他のクラスの為に基底クラスとして使用することができるだけなので、抽象クラスのオブジェクトタイプを作成することはできません。

純粋仮想関数を一つでも含むクラスは、抽象クラスとなります。したがって、抽象クラスから派生したクラスは、その純粋仮想関数を実装する必要があり、そうしないと、これらは同様に抽象クラスになります。

仮想関数は、純粋指定子構文を使用し、『純粋』として宣言されます。例として、共通機能を提供する為だけ型に作成されるCAnimalクラスを見てみましょう。CAnimalのオブジェクト自体は、実際の使用には一般的すぎます。したがって、CAnimalクラスは、抽象クラスの為の良い例となります。

class CAnimal
 {
public:
                     CAnimal();     //コンストラクタ
  virtual void       Sound() = 0;   // 純粋仮想関数
private:
  double             m_legs_count; // 動物の足の数
 };

ここでのSound()関数は、純粋仮想関数PURE(=0)の指定子で宣言されている為、純粋仮想となります。

(=NULL)または(=0)と純粋指定子PUREが指定されている仮想関数のみが純粋仮想関数となります。抽象クラスの使用と宣言例:

class CAnimal
 {
public:
  virtual void       Sound()=NULL;   // PUREメソッドは派生クラスでオーバーライドする必要があり、CAnimalクラス自体は抽象クラスになり、作成されません
 };
//--- 抽象クラスからの派生
class CCat : public CAnimal
{
public:
virtual void       Sound() { Print("Myau"); } // PUREはオーバーライドする必要があり、CCatクラスは抽象クラスではなく、作成することができます
};
 
//--- 間違った使用例
new CAnimal;         //『CAnimal』エラーは、コンパイラは「抽象クラスをインスタンス化できません」というエラーを出します
CAnimal some_animal; // CAnimal』エラーは、コンパイラは「抽象クラスをインスタンス化できません」というエラーを出します
 
//--- 正しい使用例
new CCat; // エラーはなく、CCatクラスは抽象ではありません
CCat cat; // エラーはなく、CCatクラスは抽象ではありません

 
抽象クラス使用の制限

純粋仮想関数(直接または間接)の抽象クラスのコンストラクタによる呼び出しの際、結果は未定義になります。

//+------------------------------------------------------------------+
//| 抽象基底クラス                                                       |
//+------------------------------------------------------------------+
class CAnimal
 {
public:
  //--- 純粋仮想関数
  virtual void      Sound(void)=NULL;
  //--- 関数
  void              CallSound(void) { Sound(); }
  //--- コンストラクタ
  CAnimal()
   {
    //--- 仮想メソッドの明示的な呼び出し
    Sound();
    //--- 明示的ではない呼び出し(第三の関数を介した)
    CallSound();
    //--- コンストラクタまたはデストラクタは常に自分の関数を呼び出す
    //--- 派生クラスでの呼び出された関数によるオーバーライドや仮想性に関わらず
    //--- もし、呼び出される関数が純粋仮想の場合、
    //--- 呼び出しは重大なランタイムエラーをもたらします:『純粋仮想関数呼び出し』
   }
 };

しかしながら、抽象クラスのコンストラクタとデストラクタは、他のメンバー関数を呼び出すことができます。