web-dev-qa-db-ja.com

具象クラスと抽象クラスの違いは何ですか?

私はC++を学んでいますが、抽象クラスと具象クラスについて混乱しています。いくつかの実世界の例は高く評価されるでしょう。

51

抽象クラスは、1つ以上のメソッドが宣言されているが定義されていないクラスです。つまり、コンパイラはこれらのメソッドがクラスの一部であることを認識しますが、そのメソッドに対して実行するコードは認識しません。これらは抽象メソッドと呼ばれます。以下に抽象クラスの例を示します。

class shape {
public:
  virtual void draw() = 0;
};

これは、クラスが具象である場合、クラスの子孫がdrawメソッドを実装することを指定する抽象クラスを宣言します。このクラスは抽象的であるため、インスタンス化できません。結局のところ、メンバー描画を呼び出した場合、コンパイラはどのコードを実行するのかわからないからです。したがって、次のことはできません。

shape my_shape();
my_shape.draw();

実際にdrawメソッドを使用するには、この抽象クラスからクラスを派生させる必要があります。この抽象クラスはdrawメソッドを実装し、クラスを具体化します。

class circle : public shape {
public:
  circle(int x, int y, int radius) {
    /* set up the circle */
  }
  virtual draw() {
    /* do stuff to draw the circle */
  }
};

class rectangle : public shape {
public:
  rectangle(int min_x, int min_y, int max_x, int max_y) {
    /* set up rectangle */
  }
  virtual draw() {
    /* do stuff to draw the rectangle */
  }
};

これで、コンクリートオブジェクトの円と長方形をインスタンス化し、それらの描画メソッドを使用できます。

circle my_circle(40, 30, 10);
rectangle my_rectangle(20, 10, 50, 15);
my_circle.draw();
my_rectangle.draw();

さて、もちろん問題は、なぜこれをしたいのかということです。円と長方形のクラスを定義し、形状クラス全体を廃止したのではないでしょうか?可能ですが、その継承を利用することはできません:

std::vector<shape*> my_scene;
my_scene.Push_back(new circle(40, 30, 10));
my_scene.Push_back(new rectangle(20, 10, 50, 15));
std::for_each(my_scene.begin(), my_scene.end(), std::mem_fun_ref(&shape::draw)

このコードを使用すると、すべての図形を1つのコンテナーに収集できます。これにより、シーンに多くのシェイプと多くの異なるシェイプがある場合、非常に簡単になります。たとえば、すべての図形を一度に描画できるようになりました。これを実行するコードは、さまざまな種類の図形を知る必要さえありません。

最後に、空の関数ではなく、なぜshapeのdraw関数が抽象的であるのか、つまり、なぜ定義しないのかを知る必要があります:

class shape {
public:
  virtual void draw() {
    /* do nothing */
  }
};

その理由は、形状オブジェクトのタイプが実際に必要ないことです。オブジェクトはとにかく実際のものではなく、抽象オブジェクトです。したがって、drawメソッドの実装を定義しても、空の実装を定義しても意味がありません。シェイプクラスを抽象化することで、シェイプクラスを誤ってインスタンス化したり、派生クラスの描画関数の代わりにベースクラスの空の描画関数を誤って呼び出したりすることを防ぎます。実際には、シェイプのように動作したいクラスのインターフェイスを定義します。そのようなクラスには、指定したとおりの描画メソッドが必要です。

最後の質問に答えるために、すべてのクラスが抽象的または具体的である「通常の派生クラス」というものはありません。抽象メソッドを持つクラスは抽象クラスであり、抽象メソッドを持たないクラスは具象クラスです。これは、2つのタイプのクラスを区別するための単なる方法です。基本クラスは抽象または具象にでき、派生クラスは抽象または具象にできます。

class abstract_base {
public:
  virtual void abstract_method1() = 0;
  virtual void abstract_method2() = 0;
};

class concrete_base {
public:
  void concrete_method1() {
    /* do something */
  }
};

class abstract_derived1 : public abstract_base {
public:
  virtual void abstract_method3() = 0;
};

class abstract_derived2 : public concrete_base {
public:
  virtual void abstract_method3() = 0;
};

class abstract_derived3 : public abstract_base {
public:
  virtual abstract_method1() {
    /* do something */
  }
  /* note that we do not provide an implementation for
     abstract_method2 so the class is still abstract */
};

class concrete_derived1 : public concrete_base {
public:
  void concrete_method2() {
    /* do something */
  }
};

class concrete_derived2 : public abstract_base {
public:
  virtual void abstract_method1() {
    /* do something */
  }
  virtual void abstract_method2() {
    /* do something */
  }
  /* This class is now concrete because no abstract methods remain */
};
97
wich

抽象クラ​​スはオブジェクトの作成には使用できません。一方、concrete classを使用してオブジェクトを作成できます。

コンクリートは '' ''を意味します現実に存在するまたは実際の経験;感覚によって知覚可能;リアル''。一方、abstractは「適用または実用的ではない;理論的」を意味します。

abstractクラスはインスタンス化できません。一方、concreteができます。

abstractクラスは、1つ以上の純粋な仮想関数を持つクラスです。一方、concreteクラスには純粋な仮想関数はありません。

22
Th. Manjit SEMC

具象クラスは、オブジェクトの作成に使用できるクラスです。抽象クラスを使用してオブジェクトを作成することはできません(抽象クラ​​スを拡張し、具体的なクラスを作成してからオブジェクトを作成できるようにする必要があります)。

原材料を「スタンプ」して車を作ることができる機械があると仮定します。スタンパーは具象クラスです。これから、車のオブジェクトを作成できます。抽象クラスは、スタンパーの設計図になります。スタンパーの設計図から車を作成することはできません。まず、設計図からスタンパークラスを作成する必要があります。

20
Jansen Price

抽象クラスはインスタンス化できませんが、具象クラスはできます。抽象クラスは、インスタンス化できる派生クラスの「設計図」として機能します。

例えば。 Carクラス(抽象)Audi S4クラス(Carから派生)クラスは具体的な実装です。

3
jldupont
3
Kelly S. French

基本クラスと派生クラスは、抽象クラスと具象クラスの直交概念です。

基本クラスは、他のクラスから継承しないものです。派生クラスは別のクラスから継承します。

抽象クラスは、1つ以上の純粋な仮想関数を持つクラスです。具象クラスには純粋な仮想はありません。

抽象クラスは、基本クラスまたは派生クラスのいずれかです(別の抽象クラスから派生します)。具象クラスは、ベースまたは派生のいずれかです。派生クラスに純粋な仮想関数を追加することで、具象クラスから抽象クラスを派生することもできます。ただし、一般的には、1つの基本抽象クラスと1つ以上の具体的な派生クラスがあります。

1
KeithB

抽象クラスを使用する良い例は、非常にモジュール化されたものを構築する場合です。データストアを使用しているが、そのデータはMySQLデータベース、SQLiteデータベース、XMLファイル、またはプレーンテキストにあるとします。コードでこの汎用性を維持するには、データストアから情報を取得するために使用するパブリックメソッドを定義するクラスAbstractDatastoreを作成します。次に、AbstractDatastoreXmlDatastoreなどのSQLiteDatastoreの特定の実装を作成します。その後、プログラムはAbstractDatastoreを取得していることを知るだけで十分です。これらの関数はAbstractDatastoreで定義されている必要がありますが、データがどのように保存または取得されるかを認識または気にしません。

1
Corey D

C++ Faq Lite は、この種の質問に対する回答を探すのに最適なサイトです。

設計レベルでは、抽象基本クラス(ABC)は抽象概念に対応します。メカニックに車の修理を依頼した場合、彼はおそらくどのような車を考えていたのだろうと思うでしょう。彼はスペースシャトル、オーシャンライナー、自転車、または原子力潜水艦を修理しない可能性があります。問題は、「乗り物」という用語が抽象的な概念であるということです(たとえば、どの種類の乗り物を構築するかを知らない限り、「乗り物」を構築することはできません)。 C++では、クラスVehicleはABCで、自転車、SpaceShuttleなどが派生クラスになります(OceanLinerは一種のビークルです)。実世界のオブジェクト指向では、ABCはあちこちに現れます

抽象クラスは、1つ以上の純粋な仮想メンバー関数を持つクラスです。抽象クラスのオブジェクト(インスタンス)は作成できません

 class Shape {
 public:
   virtual void draw() const = 0;  // = 0 means it is "pure virtual"
   ...
 }; 
0
Diego Dias

具象クラスにはすべてのメソッドが実装されています。抽象クラスは、いくつかの(少なくとも1つの)未実装のメソッドを除くすべてのメソッドです。これにより、メソッドを拡張し、未実装のメソッドを実装できます。

利点:抽象クラスから拡張することにより、基本クラスのすべての機能が得られ、実装されていないメソッドを実装するように強制されます。そのため、クラスの設計者は基本的に、クラスを使用する前に抽象メソッドでコードを記述することを強制します。

0
prashant