web-dev-qa-db-ja.com

C ++での純粋仮想関数の用途は何ですか?

私は今、クラスでC++について学んでいますが、純粋仮想関数についてはあまり理解していません。後で派生クラスで概説されることを理解していますが、派生クラスで定義するだけの場合、なぜ0に等しいと宣言したいのでしょうか。

22
patricksweeney

簡単に言うと、クラスを抽象化してインスタンス化できないようにすることですが、子クラスは純粋仮想メソッドをオーバーライドして具象クラスを形成できます。これは、C++でインターフェイスを定義するための良い方法です。

34
Steven Sudit

これにより、派生クラスが関数を定義するように強制されます。

14
Jim Buck

純粋仮想メソッドを含むクラスは抽象化されます。つまり、インスタンス化できません。抽象クラスは、サブクラスが共有する必要のあるいくつかのコア動作を定義するのに役立ちますが、サブクラスが抽象を個別に実装できるようにします(実際には必要です)。

抽象クラスの例:

class Foo {

    // pure virtual, must be implemented by subclasses
    virtual public void myMethod() = 0;

    // normal method, will be available to all subclasses,
    // but *can* be overridden
    virtual public void myOtherMethod();
};

すべてのメソッドが抽象であるクラスをインターフェースとして使用でき、それに含まれるすべてのメソッドを実装することにより、すべてのサブクラスがインターフェースに準拠する必要があります。

インターフェースの例:

class Bar {

    // all method are pure virtual; subclasses must implement
    // all of them
    virtual public void myMethod() = 0;

    virtual public void myOtherMethod() = 0;
};
7
Jeff L

C++の純粋仮想メソッドは、基本的に、実装を必要とせずにinterfacesを定義する方法です。

4
none

Steven Suditの答えに追加するには:

「簡単に言うと、クラスを抽象化してインスタンス化できないようにすることですが、子クラスは純粋仮想メソッドをオーバーライドして具象クラスを形成できます。これは、C++でインターフェイスを定義するための良い方法です。」

この例は、派生クラスが使用できるいくつかのメンバー関数を定義するために使用する基本クラス(おそらくShape)があるが、Shapeのインスタンスが宣言されないようにし、ユーザーに派生クラス(Rectangle、Triangle、Pentagonなど)

RE:上記のジェフの答え

非抽象クラスには、仮想メンバー関数を含めてインスタンス化できます。実際、メンバー関数をオーバーロードする場合、これは必須です。デフォルトでは、C++は変数の実行時型を決定しませんが、仮想キーワードを使用して定義すると決定します。

このコードを検討してください(わかりやすくするために、アクセサー、ミューテーター、コンストラクターなどは含まれていません)。

class Person{
  int age;

  public:
    virtual void print(){
      cout << age <<endl;
    }
}

class Student: public Person{
  int studentID

  public:
    void print(){
      cout << age << studentID <<endl;
    }
}

このコードを実行すると、次のようになります。

 Person p = new Student();
 p.print();

virtualキーワードがないと、Studentクラスで発生するはずの年齢とstudentIDではなく、年齢のみが出力されます。

(この例は、c ++のJavaプログラマー http://www.Amazon.com/Java-Programmers-Mark-Allen-Weiss/dp/)の非常によく似た例に基づいています。 013919424X

@Steven Sudit:あなたは完全に正しいです、私は実際の継承を含めることを怠りました、doh!わかりやすくするためにアクセサーなどは含まれていませんが、今ではもっとわかりやすくしています。 3-7-09:すべて修正済み

3
chrisbunney

いくつかの種類の形状をモデル化し、すべてに明確な領域があると想像してください。すべての形状がIShape(インターフェースの場合は「I」)を継承する必要があると判断し、IShapeにはGetArea()メソッドが含まれます。

_class IShape {
    virtual int GetArea();
};
_

ここで問題:形状がGetArea()をオーバーライドしない場合、形状の面積をどのように計算する必要がありますか?つまり、最適なデフォルトの実装は何ですか?円はpi * radius ^ 2を使用し、正方形はlength ^ 2を使用し、平行四辺形と長方形はbase * heightを使用し、三角形は1/2 base * heightを使用し、ひし形、五角形、八角形などは他の式を使用します。

したがって、「形状の場合は、面積を計算する方法を定義する必要がありますが、それがどうなるかを知っている場合は気にしないでください」と、純粋仮想メソッドを定義します。

_class IShape {
    virtual int GetArea() = 0;
};
_
3
Max Lybbert

基本的に、純粋な仮想を使用してインターフェースを作成します(Javaと同様)。これは、他の部分の実装について何も知らなくても、期待する機能の種類に関する2つのモジュール(またはクラスなど)間の合意として使用できます。これにより、インターフェイスを使用している他のモジュールで何も変更することなく、同じインターフェイスを使用して簡単にプラグアンドプレイできます。

例えば:

class IStudent
{
    public:
    virtual ~IStudent(){};
    virtual std::string getName() = 0;
};


class Student : public IStudent
{
    public:
    std::string name;
    std::string getName() { return name; };
    void setName(std::string in) { name = in; };
};

class School
{
    public:
    void sendStudentToDetention(IStudent *in) {
        cout << "The student sent to detention is: ";
        cout << in->getName() << endl;
    };
};

int main()
{
    Student student;
    student.setName("Dave");

    School school;
    school.sendStudentToDetention(&student);
return 0;
}

学校は生徒の名前を設定する方法を知る必要はありません。学校が知る必要があるのは、生徒の名前を取得する方法だけです。生徒が実装するインターフェースと学校が使用するインターフェースを提供することにより、学校がその仕事を遂行するために必要な機能について、2つの部分の間で合意が得られます。これで、学校に影響を与えることなく、Studentクラスのさまざまな実装を切り替えて切り替えることができます(毎回同じインターフェースを実装している限り)。

1
davidivins

抽象クラスの考え方は、その型で宣言された変数(つまり、静的型)を引き続き使用できるということですが、変数は実際には実際の具象型(動的型)を参照または指します。

C++でメソッドを呼び出す場合、コンパイラーは、メソッドがそのオブジェクトでサポートされることを確認する必要があります。

純粋仮想関数を宣言することにより、コンパイラが「ああ...この変数によって参照されるものはすべてその呼び出しを受け入れることを知っています」と言うために使用できる「プレースホルダー」を配置します。これは、実際の具象型が実装されるためです。それ。ただし、抽象型で実装を提供する必要はありません。

何も宣言しなかった場合、コンパイラーには、すべてのサブタイプによって実装されることを保証する効果的な方法がありません。

もちろん、クラスを抽象化する理由を尋ねる場合は、その周りに多くの情報があります。

0
Uri