4

C++には次のクラス階層があります。

class Base {
    virtual void apply() = 0;
};

class Derived : public Base {
    virtual void apply() {
        // implementation here that uses derived_specialty
    }

    virtual void derived_specialty() = 0;
};


class Implementation : public Derived {   
    virtual void derived_specialty() {
        // implementation
    }
};

実装レベルのクラスがapplyの独自の実装を提供せず、derived_specialtyのみを実装することを保証したいと思います。Derivedから継承するクラスがapplyを実装しないことを保証する方法はありますか?そのため、Derived :: apply実装が使用されますか?私の理解では、C ++では、Baseクラスで仮想化されたメソッドは、継承階層のずっと下まで仮想化されていますが、C ++で達成するためのトリックがある場合は、それらについて聞きたいと思います。

私はいつもC++で許可されていることに驚いているので、尋ねる価値があると思いました。:)

4

8 に答える 8

21

Derivedのスペシャライゼーションではなく、Implementationをデリゲートクラスにすることができます

class Derived : public Base
{
    Derived()

    void apply() 
    {
        //whatever, delegate to impl class instance
        impl->apply_specialization();
    }


    Impl* impl;
};

class Impl : public WhateverImplInterface
{
      void apply_specialization(){}
};

その場合、実装は適用関数にアクセスできず、階層から分離されます。次に、Derivedクラスは、Implクラスのインスタンスによってパラメーター化されます。

于 2009-02-14T21:48:11.003 に答える
5

「実装レベルのクラスが独自のapplyの実装を提供しないことを保証したいと思います。」

できません。

これまでに見た例のいずれも、派生クラスが独自の適用関数を定義することを妨げるものではありません。これらはすべて、applyとderived_specialtyの関係をモデル化するためのメソッドを提供し、applyをオーバーライドしてはならないことをユーザーに提案します。ただし、一連のドキュメントでも同じことを実現できます。

あなたが探しているのは、C ++には存在しないJavaの最終ステートメントですよね?

于 2009-02-14T23:43:53.517 に答える
5

あなたは作曲によってそれをすることができます:

class Base {
    virtual void apply();
};

class Derived : public Base {

    class IImplementation {
        virtual void derived_specialty() = 0;
    };

    IImplementation& m_implementation;

    Derived(IImplementation& implementation)
        : m_implementation(implementation)
    {}

    virtual void apply() {
        // implementation here that uses m_implementation.derived_specialty
    }

};


class Implementation : Derived::IImplementation {   
    virtual void derived_specialty() {
        // implementation
    }
};

他のクラスは引き続きDerivedをサブクラス化し、applyメソッドをオーバーライドできますが、Implementationクラスはこれらのクラスの1つではなくなります。

于 2009-02-14T21:41:56.353 に答える
5

ドキュメントで制限を明確にします。

于 2009-02-14T21:46:27.840 に答える
4

Base :: applyを非仮想にし、Base内でテンプレートメソッドパターンを使用することもできます。

この記事では、この方法の利点について説明します:
http ://www.gotw.ca/publications/mill18.htm

于 2009-02-14T21:49:53.307 に答える
4

applyがオーバーライドされていないことを確認するために、アサーションをデストラクタに入れることができます。

class Base {
    virtual void apply() = 0;
};

class Derived : public Base {
    virtual void apply() {
        // implementation here that uses derived_specialty
    }
    virtual ~Derived() {
        assert(this->apply == Derived::apply);
    }
    virtual void derived_specialty() = 0;
};


class Implementation : public Derived {   
    virtual void derived_specialty() {
        // implementation
    }
};

ここでの考え方は、 this->apply が仮想テーブルからメソッド アドレスを取得し、Derived::apply がコンパイル時に解決するというものです。それらが等しい場合、apply は実装クラスで再度オーバーライドされませんでした。このアプローチには、生成されたコードから assert() マクロが削除される (削除する必要がある) リリース ビルドでパフォーマンスが低下しないという利点もあります。

于 2009-02-15T00:01:28.217 に答える
1

アクセス修飾子は常にあります。

 class base {
      protected: virtual void real_apply() = 0;
 };
 class derived : public base {
      void real_apply();
 public:
      apply() { real_apply(); }
 };
 class other : public derived {
      void func() {
          apply();      // this is ok
          real_apply(); // this is a compile time error
      }
 };
于 2009-05-05T19:02:27.413 に答える