2

質問はC++のよくある質問からです。

http://www.parashift.com/c++-faq-lite/protected-virtuals.html

パブリックオーバーロード仮想を使用したコード:

class Base {
public:
  virtual void f(int x);    ← may or may not be pure virtual
  virtual void f(double x); ← may or may not be pure virtual
};

Public Overloaded Non-Virtuals Call Protected Non-Overloaded Virtualsイディオムを介してこれを改善する:

class Base {
public:
  void f(int x)    { f_int(x); }  ← non-virtual
  void f(double x) { f_dbl(x); }  ← non-virtual
protected:
  virtual void f_int(int);
  virtual void f_dbl(double);
};

著者は言った:

パブリックオーバーロードされた非仮想呼び出し保護された非オーバーロード仮想イディオムの考え方は、パブリックオーバーロードされたメソッドを非仮想に変更し、それらの呼び出しを保護された非オーバーロード仮想にすることです。

しかし、このイディオムがどのようにリスクを改善するかについて著者が言ったことを私は理解していません。

このイディオムは、非表示ルールを適切に管理する複雑さを基本クラス(単数)に詰め込みます。これは、派生クラス(複数形)が多かれ少なかれ自動的に非表示ルールを処理することを意味します。したがって、これらの派生クラスを作成するさまざまな開発者は、派生クラス自体の詳細にほぼ完全に集中できます。微妙でしばしば誤解されている)隠蔽ルール。これにより、派生クラスの作成者が非表示ルールを台無しにする可能性が大幅に減少します。

なぜこれが隠蔽の問題を解決するのですか?私が理解していることから、名前の非表示は、メンバー関数が「仮想」であるかどうかとは関係ありません。'base'の派生クラスが関数f()を書き換えた場合でも、f(int)とf(double)は非表示になりますよね?

このイディオムから私が見ることができるのは、作者が「ベース」仮想f()を非仮想に変更し、イディオムの名前が言ったように、ヘルパー関数f_int()、f_dbl()を「保護された仮想」に配置することだけです。それはまだ何もしませんが、逆に基本クラスのポインタ/参照から動的バインディングの可能性を排除します。このイディオムの真のメリットは何ですか?

アップデート

ケレク、あなたはこれを言っていますか?私はあなたの答えの2番目の段落を完全には理解していません。例を挙げていただけますか?

class base {
public: 
    virtual void f(int x);
    virtual void f(double x);
}

class derived : public base {
public:
    virtual int f(int x); // oops, will hide base::f(int x) AND base::f(double x)
}

base *bp = new base();
base *dp = new derived();
bp->f(int i);    // ok
dp->f(int i);    // surprise!
dp->f(double d); // compile error!


class Base {
public:
    void f(int x)    { f_int(x); }  
    void f(double x) { f_dbl(x); } 
protected:
    virtual void f_int(int);
    virtual void f_dbl(double);
};

class derived : public base {
public:
    // nothing to override here 'cause f() is non virtual
protected:
    // because f_int() and f_dbl are unique names, override or hide f_int() will not affect f_dbl()?
    virtual int f_int(int);  // oops, will hide base::f(int x), but developer may want this on purpose
                             // no effect on f_dbl(), which is good
}

base bobj;
derived dobj;
bobj.f(int i);    // ok
dobj.f(double d); // ok
4

3 に答える 3

2

派生クラスがを宣言する場合、void f(int)それは仮想関数をオーバーライドvirtualし、指定子が暗黙指定されます。派生クラスがを宣言する場合int f(int)、基本関数を非表示にします。あなたはこれに精通していると思います。

問題は、基本クラスに基づいて他の人にコードを開発してもらいたい場合に発生します。素朴なアプローチでは、各派生クラスは、誤って関数を非表示にして機能するが間違ったプログラムを取得しないように、正しいオーバーライドを追加するように注意する必要があります(つまり、ユーザーが言うf()が間違ったことを取得します)。「パブリック非仮想」イディオムを使用すると、ユーザーは常に自信を持って電話をかけることができます。ライブラリ開発者は、他のユーザーf()に影響を与える可能性のある名前に触れることなく、一意の名前の保護された関数をオーバーライドすることで、関心のある部分のみをオーバーライドできます。。

于 2012-08-06T08:53:47.487 に答える
0

SomeDerived::f()私が見ることができるのは、仮想を使用するのではなく、リンクしている可能性を減らすことだけですがBase::f()、これまでのところ、コンパイラーがこれについて有罪となったことは覚えていません。より良い答えを提案する他の誰かを探しています...

于 2012-08-06T08:53:02.263 に答える
0

少なくとも歴史的に、非公開の仮想関数を使用する主な動機は、契約によるプログラミングをサポートすることでした。の(非仮想)パブリック関数は、事前条件と事後条件および不変条件(または類似のもの)をBase使用してコントラクトを定義 します。assertそれらは、実際の作業のためにプライベートまたは保護された仮想関数に転送されます。したがって、たとえば、古典的なclone関数は次のように定義されます。

class Base
{
    virtual Base* doClone() const = 0;
public:
    Base* clone() const
    {
        Base* results = doClone();
        assert( typeid(*this) == typeid( *results ) );
        return results;
    }
};

の場合clone、この種の保護はおそらくやり過ぎです。派生クラスが誤って実装している(または、派生のレベルが複数ある場合は実装に失敗している)ため、問題はまだ発生していません。ただし、他のほとんどの機能では、堅牢なソフトウェアを開発するための強力で効果的な手段です。

インターフェイスにオーバーロードされた関数がありそれらの実装が異なる場合、仮想関数をオーバーロードしない本当の理由はわかりません。

于 2012-08-06T09:20:15.227 に答える