私は今、クラスでC ++について学んでいますが、純粋仮想関数についてはあまり理解していません。後で派生クラスで概説されることを理解していますが、派生クラスで定義するだけの場合、なぜ0に等しいと宣言したいのでしょうか。
8 に答える
簡単に言うと、クラスを抽象化してインスタンス化できないようにすることですが、子クラスは純粋仮想メソッドをオーバーライドして具象クラスを形成できます。これは、C++でインターフェイスを定義するための良い方法です。
これにより、派生クラスが関数を定義するように強制されます。
純粋仮想メソッドを含むクラスはすべて抽象化されます。つまり、インスタンス化できません。抽象クラスは、サブクラスが共有する必要があるいくつかのコア動作を定義するのに役立ちますが、サブクラスが抽象を個別に実装できるようにします (実際には要求します)。
抽象クラスの例:
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;
};
C ++の純粋仮想メソッドは、基本的に、実装を必要とせずにインターフェイスを定義する方法です。
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ではなく、年齢のみが出力されます。
@Steven Sudit:あなたは完全に正しいです、私は実際の継承を含めることを怠りました、doh!わかりやすくするためにアクセサーなどは含まれていませんが、今ではもっとわかりやすくしています。3-7-09:すべて修正済み
いくつかの種類の形状をモデル化し、すべてが明確に定義された領域を持っていると想像してください。すべての形状は継承する必要がありIShape
(インターフェイスの「I」)、メソッドIShape
を含めることにしました。GetArea()
class IShape {
virtual int GetArea();
};
問題: 形状が上書きされない場合、形状の面積をどのように計算すればよいGetArea()
ですか? つまり、最適なデフォルトの実装は何ですか? 円は pi*radius^2 を使用し、正方形は長さ^2 を使用し、平行四辺形と長方形は底辺*高さを使用し、三角形は底辺*高さの 1/2 を使用し、ひし形、五角形、八角形などは他の式を使用します。
だから私は、純粋な仮想メソッドを定義することによって、「もしあなたが形なら、面積を計算する方法を定義しなければならないが、それがどうなるか知っていれば気にしない」と言います:
class IShape {
virtual int GetArea() = 0;
};
基本的に、ピュア バーチャルを使用してインターフェイスを作成します (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;
}
学校は生徒の名前を設定する方法を知る必要はありません。生徒の名前を取得する方法を知る必要があるだけです。Student が実装するインターフェイスと学校が使用するインターフェイスを提供することで、学校がその仕事を実行するために必要な機能について、2 つの部分の間で合意が得られます。これで、学校に影響を与えずに (毎回同じインターフェースを実装する限り)、必要に応じて Student クラスのさまざまな実装を切り替えることができます。
抽象クラスの考え方は、その型で宣言された変数(つまり、静的型)を使用できるということですが、変数は実際には実際の具象型(動的型)を参照または指します。
C ++でメソッドを呼び出す場合、コンパイラーは、メソッドがそのオブジェクトでサポートされていることを確認する必要があります。
純粋仮想関数を宣言することで、コンパイラが「ああ...この変数によって参照されるものはすべてその呼び出しを受け入れることを知っています」と言うために使用できる「プレースホルダー」を配置します。これは、実際の具象型が実装されるためです。それ。ただし、抽象型で実装を提供する必要はありません。
何も宣言しなかった場合、コンパイラには、すべてのサブタイプによって実装されることを保証する効果的な方法がありません。
もちろん、クラスを抽象化する理由を尋ねる場合は、その周りに多くの情報があります。