144

C++でプライベートメソッドを仮想化する利点は何ですか?

オープンソースの C++ プロジェクトでこれに気づきました。

class HTMLDocument : public Document, public CachedResourceClient {
private:
    virtual bool childAllowed(Node*);
    virtual PassRefPtr<Element> createElement(const AtomicString& tagName, ExceptionCode&);
};
4

5 に答える 5

138

Herb Sutter はここでとてもうまく説明しています。

ガイドライン #2: 仮想機能を非公開にすることを優先します。

これにより、派生クラスは関数をオーバーライドして、必要に応じて動作をカスタマイズできます。仮想関数を派生クラスによって呼び出し可能にすることで、仮想関数を直接公開する必要はありません (関数が単に保護されている場合に可能になります)。ポイントは、カスタマイズを可能にする仮想機能が存在することです。派生クラスのコード内から直接呼び出す必要がない限り、非公開にする必要はありません。

于 2010-01-31T05:42:55.380 に答える
74

メソッドが仮想の場合、プライベートであっても、派生クラスによってオーバーライドできます。仮想メソッドが呼び出されると、オーバーライドされたバージョンが呼び出されます。

(Prasoon Saurav が回答で引用した Herb Sutter とは反対に、C++ FAQ Liteはプライベート仮想化に反対することを推奨しています。これは主に、人々を混乱させることが多いためです。)

于 2010-01-31T05:46:43.580 に答える
12

仮想メンバーをプライベートに宣言するためのすべての呼び出しにもかかわらず、議論は単に水を保持していません。多くの場合、仮想関数の派生クラスのオーバーライドは、基本クラス バージョンを呼び出す必要があります。宣言されている場合はできませんprivate:

class Base
{
 private:

 int m_data;

 virtual void cleanup() { /*do something*/ }

 protected:
 Base(int idata): m_data (idata) {}

 public:

 int data() const { return m_data; }
 void set_data (int ndata) { m_data = ndata; cleanup(); }
};

class Derived: public Base
{
 private:
 void cleanup() override
 {
  // do other stuff
  Base::cleanup(); // nope, can't do it
 }
 public:
 Derived (int idata): base(idata) {}
};

基本クラスのメソッドを宣言する必要がありますprotected

次に、コメントを介して、メソッドをオーバーライドする必要があるが呼び出さないことを示すという醜い手段を取る必要があります。

class Base
{
 ...
 protected:
 // chained virtual function!
 // call in your derived version but nowhere else.
 // Use set_data instead
 virtual void cleanup() { /* do something */ }
 ...

したがって、ハーブ・サッターのガイドライン #3...しかし、いずれにせよ、馬は納屋から出ています。

何かを宣言するprotectedと、派生クラスの作成者が保護された内部構造を理解して適切に使用することを暗黙的に信頼します。これは、宣言がメンバーfriendに対するより深い信頼を意味するのと同じです。private

その信頼に反して悪い振る舞いをするユーザー (たとえば、わざわざドキュメントを読まないことで「無知」というレッテルを貼られるなど) は、自分自身に責任があります。

更新:プライベート仮想関数を使用して、この方法で仮想関数の実装を「チェーン」できると主張するフィードバックがいくつかありました。もしそうなら、私はそれを見てみたいです。

私が使用する C++ コンパイラでは、派生クラスの実装からプライベート基本クラスの実装を呼び出すことは絶対にできません。

C++ 委員会がこの特定のアクセスを許可するために「プライベート」を緩和した場合、私はすべてプライベート仮想関数を支持します。現状では、馬が盗まれた後は納屋のドアをロックするように勧められています。

于 2016-02-25T16:00:00.127 に答える
4

私はそれらを使用して、エンドユーザーにそのような穴を公開することなく、派生クラスが基本クラスの「空白を埋める」ことができるようにします。たとえば、共通ベースから派生した非常にステートフルなオブジェクトがありますが、これはステート マシン全体の 2/3 しか実装できません (派生クラスはテンプレート引数に応じて残りの 1/3 を提供し、ベースはテンプレートにすることはできません)。その他の理由)。

パブリック API の多くを正しく動作させるには、共通の基本クラスが必要ですが (私は可変個引数テンプレートを使用しています)、そのオブジェクトを公開することはできません。さらに悪いことに、クレーターを純粋な仮想関数の形でステート マシンに残すと、"Private" 以外の場所にクレーターを残すと、その子クラスの 1 つから派生した賢いユーザーや無知なユーザーが、ユーザーが決して触れてはならないメソッドをオーバーライドできるようになります。そこで、ステート マシンの「頭脳」を PRIVATE 仮想関数に配置しました。次に、基本クラスの直接の子が非仮想オーバーライドの空白を埋めるため、ユーザーは結果のオブジェクトを安全に使用したり、ステート マシンの混乱を心配することなく独自の派生クラスを作成したりできます。

パブリック仮想メソッドを持つべきではないという議論については、私は BS と言います。ユーザーは、プライベート バーチャルをパブリック バーチャルと同じくらい簡単に不適切にオーバーライドできます。結局、ユーザーは新しいクラスを定義しているのです。パブリックが特定の API を変更してはならない場合は、パブリックにアクセス可能なオブジェクトでそれを仮想化しないでください。

于 2014-02-05T21:10:15.403 に答える