1. ホーム
  2. c++

[解決済み] プライベートな純粋仮想関数のポイントは何ですか?

2022-05-07 19:58:13

質問

ヘッダーファイルで次のようなコードを見かけました。

class Engine
{
public:
    void SetState( int var, bool val );
    {   SetStateBool( int var, bool val ); }

    void SetState( int var, int val );
    {   SetStateInt( int var, int val ); }
private:
    virtual void SetStateBool(int var, bool val ) = 0;    
    virtual void SetStateInt(int var, int val ) = 0;    
};

私には、このことは Engine クラスまたはその派生クラスは、これらの純粋仮想関数の実装を提供する必要があります。しかし、派生クラスがこれらのプライベート関数を再実装するためにアクセスできるとは思えないのですが、なぜ仮想関数にするのでしょうか?

どうすればいい?

このトピックの質問は、かなり一般的な混乱を示唆しています。その混乱は十分に一般的であり C++のFAQ というのも、混乱は悪いことのように思われるからです。

はい、プライベート仮想関数は派生クラスでオーバーライドできます。派生クラスのメソッドは、基底クラスの仮想関数を呼び出すことはできませんが、その実装を独自に用意することは可能です。Herb Sutterによると、ベースクラスで非仮想的なパブリックインターフェースを持ち、派生クラスでカスタマイズ可能なプライベートな実装を持つことで、インターフェースの仕様と実装のカスタマイズ可能な動作の仕様の分離をより良くすることができる"だそうです。詳しくは、彼の記事 バーチャリティ" .

しかし、あなたが提示したコードにはもう一つ興味深い点があり、それはもう少し注意を払うべきだと私は思います。パブリックインターフェースはオーバーロードされた非仮想関数のセットで構成され、それらの関数は非パブリック、非オーバーロードの仮想関数を呼び出します。C++の世界の常として、これはイディオムであり、名前がついていて、もちろん便利なものです。その名は(驚きの、驚きの!)。

Public Overloaded Non-Virtuals Call Protected Non-Overloaded Virtuals"

に役立ちます。 隠蔽ルールを適切に管理する . 詳しくはこちら こちら ということですが、簡単に説明します。

の仮想関数があるとします。 Engine クラスはそのインターフェイスでもあり、純粋仮想ではないオーバーロードされた関数の集合です。もしそれらが pure virtual であったとしても、以下のように、クラス階層の下位で、同じ問題に遭遇する可能性があります。

class Engine
{
public:
    virtual void SetState( int var, bool val ) {/*some implementation*/}
    virtual void SetState( int var, int val )  {/*some implementation*/}
};

ここで、派生クラスを作成し、引数として2つのintを受け取るメソッドのみを新しく実装する必要があるとします。

class MyTurbochargedV8 : public Engine
{
public:
    // To prevent SetState( int var, bool val ) from the base class,
    // from being hidden by the new implementation of the other overload (below),
    // you have to put using declaration in the derived class
    using Engine::SetState;

    void SetState( int var, int val )  {/*new implementation*/}
};

もし、派生クラスにusing宣言を入れ忘れた(あるいは2つ目のオーバーロードを再定義し忘れた)場合、以下のようなシナリオで問題になる可能性があります。

MyTurbochargedV8* myV8 = new MyTurbochargedV8();
myV8->SetState(5, true);

の非表示を防がなかった場合は、「1. Engine のメンバーであれば、この文は

myV8->SetState(5, true);

を呼び出します。 void SetState( int var, int val ) を派生クラスから変換して true から int .

この例のように、インターフェイスが仮想ではなく、仮想実装が非公開であれば、派生クラスの作者は考えるべき問題がひとつ減り、単に

class MyTurbochargedV8 : public Engine
{
private:
    void SetStateInt(int var, int val )  {/*new implementation*/}
};