序文:
これは、継承された親の仮想メソッドをオーバーライドする子クラスに適用された場合の C++11 で導入された削除演算子の新しい意味に関するベスト プラクティスに関する質問です。
バックグラウンド:
標準によると、引用されている最初の使用例は、最新のC++11 標準ドラフトの §8.4.3 の例のように、変換が暗黙的に行われる特定の型の関数の呼び出しを明示的に禁止することです。
struct sometype {
sometype() = delete; // OK, but redundant
some_type(std::intmax_t) = delete;
some_type(double);
};
上記の例は明確で目的があります。ただし、新しい演算子がオーバーライドされ、削除済みとして定義することで呼び出されないようにする次の例では、後で質問セクションで特定する他のシナリオについて考え始めました (以下の例は、C++の §8.4.3 からのものです)。 11 標準ドラフト):
struct sometype {
void *operator new(std::size_t) = delete;
void *operator new[](std::size_t) = delete;
};
sometype *p = new sometype; // error, deleted class operator new
sometype *q = new sometype[3]; // error, deleted class operator new[]
質問:
この考えを継承に拡張することで、次の使用例が明確で有効なユースケースなのか、それとも新しく追加された機能の不明確な悪用なのかについて、他の人の考えに興味があります。回答の根拠を示してください (最も説得力のある理由を示す例が採用されます)。次の例では、ライブラリの 2 番目のバージョンが最初のバージョンから継承されるようにすることで、ライブラリの 2 つのバージョンを維持しようとします (ライブラリはインスタンス化する必要があります)。アイデアは、最初のライブラリ バージョンに加えられたバグ修正または変更が自動的に 2 番目のライブラリ バージョンに反映されるようにし、2 番目のライブラリ バージョンが最初のバージョンとの違いのみに集中できるようにすることです。2 番目のライブラリ バージョンで関数を非推奨にするには、
class LibraryVersion1 {
public:
virtual void doSomething1() { /* does something */ }
// many other library methods
virtual void doSomethingN() { /* does something else */ }
};
class LibraryVersion2 : public LibraryVersion1 {
public:
// Deprecate the doSomething1 method by disallowing it from being called
virtual void doSomething1() override = delete;
// Add new method definitions
virtual void doSomethingElse() { /* does something else */ }
};
このようなアプローチには多くの利点がありますが、機能の悪用であると考える傾向が強いと思います。上記の例に見られる主な落とし穴は、継承の古典的な「is-a」関係が壊れていることです。私は多くの記事を読んだことがありますが、継承を使用して「一種の」関係を表現することを強く推奨し、代わりにラッパー関数を使用して構成を使用してクラスの関係を明確に識別することを強く推奨しています。次のよく眉をひそめられる例では、実装と保守にさらに多くの労力が必要ですが (このコード部分に記述された行数に関しては、パブリックに使用できるすべての継承された関数は、継承クラスによって明示的に呼び出される必要があるため)、上記の削除は、多くの点で非常に似ています。
class LibraryVersion1 {
public:
virtual void doSomething1() { /* does something */ }
virtual void doSomething2() { /* does something */ }
// many other library methods
virtual void doSomethingN() { /* does something */ }
};
class LibraryVersion2 : private LibraryVersion1 {
// doSomething1 is inherited privately so other classes cannot call it
public:
// Explicitly state which functions have not been deprecated
using LibraryVersion1::doSomething2();
// ... using (many other library methods)
using LibraryVersion1::doSomethingN();
// Add new method definitions
virtual void doSomethingElse() { /* does something else */ }
};
この潜在的な削除のユースケースについての回答とさらなる洞察を前もって感謝します。