抽象クラス
インスタンス化はできないけれども基底クラスとしては使用できる抽象型を定義します。
構文
declarator virt-specifier(オプション) = 0
|
|||||||||
このシーケンス = 0 は純粋指定子と言い、 declarator の直後またはオプショナルな virt-specifier (override または final) の後のいずれかに現れます。
純粋指定子はメンバ関数の定義に現れることはできません。
struct Base { virtual int g(); virtual ~Base() {} };
struct A : Base {
// OK、3つのメンバ仮想関数を宣言し、そのうち2つは純粋です。
virtual int f() = 0, g() override = 0, h();
// OK、デストラクタも純粋にできます。
~A() = 0;
// エラー、関数定義における純粋指定子。
virtual int b()=0 {}
};
抽象クラスは、最終オーバーライダーが純粋仮想である関数を少なくともひとつ定義または継承するクラスです。
説明
抽象クラスは汎用的な概念 (例えば形状や動物など) を表現するために使用されます。 これは具象クラス (例えば円や犬など) に対する基底クラスとして使用することができます。
抽象クラスのオブジェクトを作成することはできず (そこから派生したクラスの基底部分オブジェクトの場合は除きます)、抽象クラスの非静的データメンバを宣言することはできません。
抽象型は引数の型、関数の戻り値の型、または明示的な変換の型として使用することはできません (関数宣言の時点では引数および戻り値の型は不完全かもしれないため、これは定義および関数呼び出しの時点でチェックされることに注意してください)。
抽象クラスへのポインタおよび参照は宣言できます。
struct Abstract {
virtual void f() = 0; // 純粋仮想関数。
}; // 「Abstract」は抽象クラスです。
struct Concrete : Abstract {
void f() override {} // 純粋でない仮想関数。
virtual void g(); // 純粋でない仮想関数。
}; // 「Concrete」は抽象クラスではありません。
struct Abstract2 : Concrete {
void g() override = 0; // 純粋仮想オーバーライダー。
}; // 「Abstract2」は抽象クラスです。
int main()
{
// Abstract a; // エラー、抽象クラスです。
Concrete b; // OK。
Abstract& a = b; // 抽象基底を参照するのは OK。
a.f(); // Concrete::f() への仮想ディスパッチ。
// Abstract2 a2; // エラー、抽象クラスです (g() の最終オーバーライダーは純粋です)。
}
純粋仮想関数は定義を提供しても構いません (その純粋仮想がデストラクタの場合は提供しなければなりません)。 派生クラスのメンバ関数は修飾された関数の識別子を用いて基底クラスの純粋仮想関数を自由に呼べます。 この定義はクラス本体の外側で提供されなければなりません (関数宣言の構文は純粋指定子 = 0 と関数本体の両方は使用できません)。
抽象クラスのコンストラクタまたはデストラクタからの純粋仮想関数への仮想呼び出しを行うことは未定義動作です (それが定義を持つか否かにかかわらず)。
struct Abstract {
virtual void f() = 0; // 純粋仮想関数。
virtual void g() {} // 純粋でない仮想関数。
~Abstract() {
g(); // OK、 Abstract::g() を呼びます。
// f(); // 未定義動作。
Abstract::f(); // OK、仮想でない呼び出し。
}
};
// 純粋仮想関数の定義。
void Abstract::f() { std::cout << "A::f()\n"; }
struct Concrete : Abstract {
void f() override {
Abstract::f(); // OK、純粋仮想関数を呼びます。
}
void g() override {}
~Concrete() {
g(); // OK、 Concrete::g() を呼びます。
f(); // OK、 Concrete::f() を呼びます。
}
};