4

Depクラスのツリーの基本クラスがあるとします。Dep* Dep::create()すべてのリーフ クラスで実装したい仮想メソッドがあります。これを強制する方法はありますか?

注: ここでの問題は、中間クラス (など) がこのメソッド ( ) を誤ってclass B : public A : public Dep実装している可能性があること、または中間クラスがリーフ クラスであると考えているために実際にはサブクラス化されている可能性があることです。A::create

質問はここまでです。

環境

なぜ私がこれを必要とするのか興味があるなら; 未知の具象型のオブジェクトを持つクラスMasterがあります。Depが重複している場合Masterは、一致するDepインスタンスのクローンを作成する必要があります。次善の策は、まさにこの問題を引き起こす仮想コンストラクターのイディオムです。

さらに、私はこれをキャッチすることさえできません (恐ろしくクラッシュする以外)。なぜなら、あいまいな理由で、私よりも言いたいことがある人がdynamic_castこのプロジェクトを非合法化したからです (おそらくこれは良い決定ですが、とにかくまったく別の議論です)。

4

5 に答える 5

4

C++ には、クラスが自分のクラスから継承されないようにする方法はありません。また、継承階層内の特定のクラスにメソッドを実装させる方法もありません。唯一の規則は、特定のクラスより上の継承階層のどこか(必ずしもリーフである必要はありません) に、すべての仮想関数がそのクラスをインスタンス化できるように実装されている必要があるということです。

たとえば、すべての [純粋な] 仮想メソッドAを継承して実装できます。から継承するDef場合、何も実装する必要はありません。それが起こらないようにする方法はありません。BA

したがって、答えはノーです。これを強制する方法はありません。

于 2011-09-02T20:17:42.230 に答える
3

不思議なことに繰り返されるテンプレート fun を使用すると、非常によく似たことが実現できます。

template<typename T>
class Cloneable : public T, public Dep
{
private:
    Cloneable<T>() : T() { }
public:
    static Cloneable<T>* Create() { return new Cloneable<T>(); }
    Cloneable<T>* clone() { return new Cloneable<T>(*this); }
};

から派生しDepてインスタンス化する代わりにnew MyType、 を使用しますCloneable<MyType>::Create。はからCloneable<MyType>派生しているため、 が保証されていることを除いて、MyType任意の を使用するのと同じ方法でインスタンスを使用できます。MyTypeDep::clone

さらに、Mastertype のインスタンスを受け入れるべきではありませんDepが、それが であることを強制する必要がありCloneable<T>ます。(元の関数を、これを強制する単純な関数テンプレートに置き換えます。) これによりDep、マスター内のすべての関数が正しく実装されていることが保証されcloneます。

Cloneable<MyType>public コンストラクターがないため継承できませんが、actualはMyTypeさらに継承して以前と同じように使用できます。

于 2011-09-03T16:52:43.310 に答える
2

TPTB はすべてのRTTI を禁止しましたか、それとも 1 つだけdynamic_cast<>()ですか? RTTI を使用できる場合は、呼び出しの事後条件としてメソッドの存在を強制できます。

#include <typeinfo>
#include <cassert>
#include <iostream>
#include <stdexcept>

class Base {
protected:
  virtual Base* do_create() = 0;
  virtual ~Base() {}
public:
  Base* create() {
    Base *that = this->do_create();
    if( typeid(*this) != typeid(*that) ) {
      throw(std::logic_error(std::string() +
                             "Type: " +
                             typeid(*this).name() +
                             " != " +
                             typeid(*that).name()));
    }
    return that;
  }
};

class Derive1 : public Base {
protected:
  Base* do_create() { return new Derive1(*this); }
};

class Derive2 : public Derive1 {};

void f(Base*p) { std::cout << typeid(*p).name() << "\n"; }
int main() {
  Derive1 d1;
  Base *pD1 = d1.create(); // will succeed with correct semantics
  Derive2 d2;
  Base *pD2 = d2.create(); // will throw exception due to missing Derive2::do_create()
}
于 2011-09-02T20:47:53.370 に答える
2

基本クラスを制御する場合はAbstractDep、クラス テンプレートを使用して具体的なリーフ クラスを作成する必要があることを強制できますWithCloning。このリーフは、継承できないようにシールすることができます。より正確には、派生クラスのインスタンスを作成することはできません。

class AbstractDep
{
template< class Type > friend class WithCloning;
private:
    enum FooFoo {};
    virtual FooFoo toBeImplementedByLeafClass() = 0;

public:
    virtual AbstractDep* clone() const = 0;
};

template< class Type > class WithCloning;

class Sealed
{
template< class Type > friend class WithCloning;
private:
    Sealed() {}
};

template< class Type >
class WithCloning
    : public Type
    , public virtual Sealed
{
private:
    AbstractDep::FooFoo toBeImplementedByLeafClass()
    {
        return AbstractDep::FooFoo();
    }

public:
    virtual WithCloning* clone() const
    {
        return new WithCloning( *this );
    }
};

typedef WithCloning<AbstractDep>            Dep;


class AbstractDerivedDep
    : public AbstractDep
{
// AbstractDep::FooFoo toBeImplementedByLeafClass(); // !Not compile.
public:
};

typedef WithCloning<AbstractDerivedDep>     DDep;


struct Foo: Dep {};       // !Does not compile if instantiated.

int main()
{
    Dep     d;
    //Foo     f;
}

クラスがデフォルトの構築以上のものを必要とする場合、そのほとんどは追加で解決されます。

1 つの解決策は、コンストラクターから引数パックを転送することWithCloningです (私のブログに C++98 の実装例があり、C++0x はそれを直接サポートしています)。

要約すると、クラスをインスタンス化できるようにするには、WithCloning.

乾杯 & hth.,

于 2011-09-02T22:28:40.590 に答える
0

それらが不明であると言うとき、それらはまだ共通の基本クラス/インターフェイスから継承していると思いますか?

強制するために使用できると私が考えることができるのは、仮想基本クラスの追加に関するものだけです

virtual Base* clone() {
    assert( 1==0 ); //needs to be overriden
}

したがって、オーバーライドを余儀なくされますが、これは、オーバーライドが欠落しているクラスのインスタンスで clone を呼び出そうとしたときに実行時にのみ検出されます

dynamic_cast または RTTI の使用が許可されていない場合でも、問題のあるクラスの typeid を見つけるのに役立つ場合は、ビルドでローカルにデバッグ目的で有効にすることができます。

あなたはクローン パターンに精通しているように聞こえますが、ここに静かに投稿します

于 2011-09-02T20:20:13.213 に答える