2009-07-01 16 views
17

私は今クラスでC++について学んでいます。純粋な仮想関数をあまりにも悪化させることはありません。私は後で派生クラスで概説されていることを理解していますが、派生クラスで定義しようとしているのであれば、それを0に等しいと宣言したいのはなぜですか?C++での純粋仮想関数の使用方法は?

+2

あなたとあなたを投票した少数の人のために:テキストはWHYを説明しませんでした。他の誰かがGoogleにそれを確信させるので、私はここでそれに尋ねた。そうすれば誰かが利益を得ることができます。 – patricksweeney

+0

その教科書はなんですか?率直に言えば、ここでの答えは(そのような一般的な質問に典型的な)特にあまり良いものでも正確でもありません。このような質問をしてサービスをしているとは思わないでください。 –

+0

あなたは基本クラスを抽象抽象にしたい理由が不思議に思うかもしれません。クラス階層の使用を制御することは重要ですが、問題をモデリングするだけでなく、今後どのように使用されるのかも考慮する必要があります。 –

答えて

30

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

+1

私は変更をお勧めしたいと思います: "...子クラスができます..."から "...葉クラスは必須です..."。 –

+0

私は意図的に漠然としていて、純粋な仮想メソッドの一部をオーバーライドする可能性を許容するようにしました。その結果、オーバーライドが少なくなる抽象基本クラスが作成されます。しかし、私はそれを葉と呼んではいません。なぜなら、具体的な子供でさえ、独自の子供を持つことができ、そのうちのいくつかは抽象的であるからです。実際には、クラスは、以前の純粋な仮想メソッドをすべて同時に上書きし、新しいメソッドを宣言することができます。このことを考えれば、元々の簡単な説明をこの長いコメントで残しておく方がいいでしょう。 –

+1

問題はたぶん用語の1つに過ぎません。 IMHOでは、派生クラスの1つに実装されていない関数は純粋な仮想のみです。言い換えれば、あるレベルのクラスがまだ純粋な仮想関数を持っている場合、そのクラスは引き続き抽象クラスです。したがって、私の要求。しかし、私はこれらのコメントがおそらくこの時点で十分なことを説明していると言います。 :) –

12

これは、派生クラスに関数を定義させます。

0

抽象クラスのアイデアは、その型で宣言された変数(つまり静的型)を持つことができますが、実際には実際の具体的な型(動的型)を参照または指しています。

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

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

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

もちろん、クラスを抽象的にしたいと思っているのであれば、そこにはたくさんの情報があります。

4

C++の純粋仮想メソッドは、基本的には、インターフェイスを実装する必要なく定義する方法です。スティーブンSuditの答えに追加するには

+1

正しい、より正確には: "すべての継承者が定義されたシグネチャに従うようにする方法" –

3

:それはインスタンス化できないように

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

派生クラスが使用できる多数のメンバー関数を定義するために使用する基本クラス(多分Shape)があっても、Shapeのインスタンスが宣言されていないようにしたい場合、非抽象クラスは、仮想メンバ関数を含めることができますし、インスタンス化することが

上ジェフの答え:

REのみ派生(、長方形、三角形であってもよいし、ペンタゴンなど)のクラスを使用します。実際には、メンバ関数をオーバーロードするためには、これはデフォルトではC++が変数の実行時の型を決定するのではなく、th virtualキーワードを使用して定義すると必須です。仮想なし

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

:このコードを実行するときに今

class Person{ 
    int age; 

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

class Student: public Person{ 
    int studentID 

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

このコードを検討(などのノート、アクセサ、ミューテータ、コンストラクタは、明瞭にするために含まれていません)キーワードでは、年齢とstudentIDではなく、年齢のみが出力されます。

(この例は、C++ foと非常に似ています。fo r javaプログラマーhttp://www.amazon.com/Java-Programmers-Mark-Allen-Weiss/dp/013919424X

@スティーブンスディット:あなたは完全に正しいです、私は実際の継承を含むことを怠った、ドッ!アクセサーなどは、物事をより明確にするためには含まれていません。私はこれをもっと明白にしました。 3-7-09:すべて固定

+0

私は、継承を含まないため、あなたの例が壊れていると信じています。そして、プライベートデータメンバの値。それの背後にあるアイデアは間違いありません。 –

+0

これは優れていますが、まだ正しくはありません。子クラスには、独自の年齢データメンバーがあってはいけません。 –

+0

また、下部のインスタンス化では、コンストラクタの周りにカッコがありません。 –

7

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

抽象クラスの例:すべてのメソッドが抽象である

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; 
}; 
+0

最初の文が「純粋な仮想メソッドを含むすべてのクラス」であると思います。 – Dan

+0

抽象基本クラスにメソッドの実装だけでなく、しかしデータメンバーも同様です。インタフェースはどちらも持っていないはずです。 C++では、これは単なる規約です。 C#またはJavaでは、それはルールです。 –

3

私は形状のいくつかの種類をモデルにしたい、とすべてが明確に定義された領域を持っている想像してみてください。その形状がない場合、私は形の面積を計算する方法:

class IShape { 
    virtual int GetArea(); 
}; 

問題:私はすべての形状がIShape(インタフェースのための「I」)を継承しなければならない、とIShapeGetArea()メソッドが含まれることを決定しますオーバーライドGetArea()?つまり、最良のデフォルト実装は何ですか?サークルはpi * radius^2を使用し、四角は長さ^ 2を使用し、平行四辺形および長方形はベース*高さを使用し、三角形は1/2ベース*高さを使用し、菱形、五角形、八角形などを使用します。

だから私は「あなたがシェイプしている場合は、面積を計算する方法を定義する必要がありますが、私はそれがどうなるか知っていればのろわ」と言う純粋仮想メソッドを定義することによって:

class IShape { 
    virtual int GetArea() = 0; 
}; 
+0

どちらの場合でもGetAreaを仮想宣言する必要はありませんか? –

+0

基本クラスで仮想宣言する必要があります。訂正してくれてありがとう。 –

1

基本的には、純粋な仮想を使用してインタフェースを作成します(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クラスの実装を、学校に影響を与えずに入れ替えることができます(毎回同じインターフェースを実装している限り)。

+0

私は、学生の "名前"データメンバーはプライベートであるか、少なくとも保護されていることをお勧めします。私はまた、指針ではなく参照によって学生を渡すことをお勧めします。最後に、実世界のStudentクラスがそのコンストラクタでその名前を使用します。 –

+0

私はそのすべてに同意します。私はこの事実を素早くタイプアップしただけで、それはちょっとした過ちだった。 – davidivins

関連する問題