私は最近、C++では純粋な仮想関数にオプションで本体を含めることができることを知りました。
そのような関数の実際の使用例は何ですか?
クラシックは純粋な仮想デストラクタです。
class abstract {
public:
virtual ~abstract() = 0;
};
abstract::~abstract() {}
他に何もする必要がなく、クラスを抽象的にしたいので純粋にしますが、派生クラスのデストラクタが明示的にクラスを呼び出すため、実装を提供する必要があります。ええ、私は知っています、かなりばかげた教科書の例ですが、それ自体は古典的です。 The C++ Programming Language の最初のエディションに含まれている必要があります。
とにかく、純粋な仮想関数を実装する機能が本当に必要なことを思い出せません。私には、この機能が存在する唯一の理由のように思われます。明示的に禁止する必要があり、Stroustrupはその理由を認識していなかったからです。
この機能が必要だと感じた場合は、おそらく設計が間違っています。
本体の有無にかかわらず純粋な仮想関数は、派生型が独自の実装を提供する必要があることを単に意味します。
基本クラスの純粋な仮想関数本体は、派生クラスが基本クラスの実装を呼び出したい場合に役立ちます。
抽象基本クラス(純粋仮想関数付き)が宣言する純粋仮想関数の実装を提供する理由の1つは、派生クラスが使用することを選択できる簡単な「デフォルト」を持つようにするためです。オプションでオーバーライドできる通常の仮想関数に比べて、これには多くの利点はありません-実際、唯一の真の違いは、「デフォルト」の基本クラス実装の使用に関して派生クラスを明示的にすることです。 :
class foo {
public:
virtual int interface();
};
int foo::interface()
{
printf( "default foo::interface() called\n");
return 0;
};
class pure_foo {
public:
virtual int interface() = 0;
};
int pure_foo::interface()
{
printf( "default pure_foo::interface() called\n");
return 42;
}
//------------------------------------
class foobar : public foo {
// no need to override to get default behavior
};
class foobar2 : public pure_foo {
public:
// need to be explicit about the override, even to get default behavior
virtual int interface();
};
int foobar2::interface()
{
// foobar is lazy; it'll just use pure_foo's default
return pure_foo::interface();
}
多くの利点があるかどうかはわかりません。おそらく、設計が抽象クラスで始まった場合、派生した具象クラスの多くが同じ動作を実装していることがわかり、その動作を移動することにしました。純粋仮想関数の基本クラス実装に。
純粋な仮想関数の基本クラス実装に共通の動作を組み込むことも、派生クラスが変更/拡張/拡張を期待される場合があるので、妥当だと思います。
C++標準委員会の元委員長である万能のハーブサッター つのシナリオを示しました 純粋な仮想メソッドの実装を提供することを検討してください。
お奨めは、個人的にはそう言います–私はそれらのどれも説得力がないと思います、そして一般にこれはC++の意味論的ないぼの1つであると考えます。 C++は、子の構築/破壊時にのみそれらを短時間公開するよりも、抽象親のvtableを構築および分解するために邪魔にならないようです次にコミュニティの専門家は満場一致で 使用しないでください を推奨しています。
1つの使用例は、クラスのコンストラクタまたはデストラクタから純粋仮想関数を呼び出すことです。
ボディを持つ仮想関数とボディを持つ純粋な仮想関数の唯一の違いは、2番目の存在がインスタンス化を妨げることです。 C++でクラスの抽象をマークすることはできません。
この質問は、OODとC++を学ぶときに本当に混乱する可能性があります。個人的に、常に頭の中で浮かんでいることの1つは、次のようなものでした。PureVirtual関数も実装する必要がある場合、そもそもなぜそれを「純粋」にするのでしょうか。なぜそれを「仮想」のままにして、ベースの実装の利点とオーバーライドの両方を引き出さないのですか?
混乱は、多くの開発者がボディなし/実装を純粋な仮想関数を定義することの主要な目標/利点と見なしているという事実に来ています。本当じゃない!ボディがないことは、ほとんどの場合、純粋な仮想関数を持つことの論理的な帰結です。純粋な仮想関数を持つことの主な利点は、契約の定義です!純粋な仮想関数を定義することにより、派生するすべての関数に、関数の独自の実装を常に強制する必要があります。この「契約の側面」は、パブリックAPIのようなものを開発している場合は特に重要です。関数を仮想化するだけでは十分ではありません。デリバティブが独自の実装を提供する必要がなくなるため、コントラクトの側面が失われる可能性があります(これはパブリックAPIの場合に制限になる可能性があります)。よく言われるように、「仮想関数はオーバーライドできますが、Pure Virtual関数をオーバーライドする必要があります。」また、ほとんどの場合、コントラクトは抽象的な概念であるため、対応する純粋な仮想関数が実装されていても意味がありません。
しかし、時々、そして人生は奇妙なため、デリバティブ間の強い契約を確立し、契約の独自の動作を指定しながら、何らかのデフォルトの実装から何らかの形で利益を得ることもできます。ほとんどの本の著者がこれらの状況に陥ることを避けることを勧めているとしても、言語は最悪の事態を防ぐための安全策を提供するために必要でした!単純な仮想関数では、契約を脱出するリスクがある可能性があるため、十分ではありません。したがって、C++が提供するソリューションは、純粋な仮想関数がデフォルトの実装も提供できるようにすることでした。
上記のSutterの記事は、Pure Virtual機能とボディの興味深いユースケースを示しています。