0

クラス StructComponent のコンストラクターは、異なるロジックを使用して、info のパスイン オブジェクトの型に基づいてメンバー変数を初期化します。ここでは、キャストを使用して、パスイン パラメーターを適切なサブクラス オブジェクトに変換します。

class StructComponent
{
public:
    StructComponent(const ClassA& info)
    {
        if (info.getType() == CLASS_B)
        {
            const ClassC& classC = dynamic_cast<const ClassC&> info;
            ...
            apply a different logic for ClassB and init member accordingly
        } else if (info.getType() == CLASS_C) {
            apply a different logic for others
            ...
        } else {
                    apply default
            }
    }
}

class ClassA
{
public:
    ClassA(...)
    {
        m_shp = CreateStructComponent();
    }   
    virtual boost::shared_ptr<StructComponent> CreateStructComponent()
    {
        return boost::shared_ptr<StructComponent> (new StructComponent(*this));     
    }

    ...
    int getType() const { return CLASS_A; }

protected:
    boost::shared_ptr<StructComponent> m_shp;
}

class ClassB : public ClassA
{
public:
    ...

    virtual boost::shared_ptr<StructComponent> CreateStructComponent()
    {
        return boost::shared_ptr<StructComponent> (new StructComponent(*this));     
    }   
    ...
    int getType() const { return CLASS_B; } 
}

class ClassC : public ClassA
{
public:
    ...

    virtual boost::shared_ptr<StructComponent> CreateStructComponent()
    {
        return boost::shared_ptr<StructComponent> (new StructComponent(*this));     
    }   
    ...
    int getType() const { return CLASS_C; } 
}

Q1> 潜在的な設計上の問題を無視したコードは正しいですか?

Q2> ClassA のすべてのサブクラスが CreateStructComponent 関数の同じ実装本体を持っていると仮定します。次のように同じコードを繰り返し実行しないようにスペースを節約できる方法はありますか?

return boost::shared_ptr<StructComponent> (new StructComponent(*this));

Q3> 使用できるより良いデザインはありますか? たとえば、StructComponent でキャストを無視できる方法はありますか?

4

2 に答える 2

2

1)いいえ、それは正しくありません、少なくとも、あなたがおそらく期待することをしません。のコンストラクターで仮想関数を呼び出します。コンストラクターを実行すると、その動的型はであるため、派生クラスでオーバーライドする関数を呼び出す代わりに、ClassA常に呼び出します。同じ理由で、呼び出しのコンストラクターでは常にに解決されます。ClassA::CreateStructComponent()ClassAClassAStructComponentgetType()ClassA::getType()

2)その問題を解決する方法はたくさんあります。コードをテンプレートに入れることができるので、タイプによって異なります。または、別の場所で初期化を行うことで、コードを複製する必要をなくすことができます。

StructComponent3)オーバーロードされたコンストラクターをClassB単純に与えてみませんClassCClassA

繰り返しになりますが、より良い解決策は、StructComponentコンストラクターを取り除き、、、、または明示的に初期化を行うことです。これにより、各タイプが希望どおりに初期化されます。初期化がそれを作成するタイプに依存する場合、初期化はコンストラクターに属しません。現在、循環依存関係があり、それを使用するすべてのタイプについて知る必要があり、それを使用するすべてのタイプについて知る必要があります。これは通常、設計に問題があることを示しています。すべてのクラスは互いに緊密に結合されています。他のタイプについて何も知らなければもっと良いでしょう。ClassAClassBClassCStructComponentStructComponentStructComponentStrictComponent

とにかく、コードを複製せずに正しい型を渡す方法を示す1つの可能な解決策がStructComponentありますが、これは良い設計ではないと思います。StructcomponentコンストラクターにはNULLが渡されるため、型に基づいてさまざまなことを実行できますが、渡されたオブジェクトにはアクセスできないことに注意してください。

class StructComponent
{
public:
    explicit StructComponent(const ClassA*)
    { /* something */ }

    explicit StructComponent(const ClassB*)
    { /* something else */ }

    explicit StructComponent(const ClassC*)
    { /* something completely different */ }
};

class Base
{
protected:
  template<typename T>
    explicit
    Base(const T*)
    : m_shp( boost::make_shared<StructComponent>((T*)NULL) )
    { }

    boost::shared_ptr<StructComponent> m_shp;
};

class ClassA : virtual public Base
{
public:
    ClassA() : Base(this) { }
};

class ClassB : public ClassA
{
public:
    ClassB() : Base(this) { }
};

class ClassC : public ClassA
{
public:
    ClassC() : Base(this) { }
};

これは、仮想ベースのハッキングを行わず、重複したコードを削除し、StructComponent渡されたオブジェクトにアクセスできるようにする、まったく異なる別のアプローチです(これは悪い考えです)。

class StructComponent
{
public:
    explicit StructComponent(const ClassA& a)
    { /* something */ }

    explicit StructComponent(const ClassB& b)
    { /* something else */ }

    explicit StructComponent(const ClassC& c)
    { /* something completely different */ }
};

class ClassA
{
public:
    ClassA() : m_shp( create(*this) ) { }

protected:
    struct no_init { };

    explicit
    ClassA(no_init)
    : m_shp()
    { }

    template<typename T>
      boost::shared_ptr<StructComponent> create(const T& t)
      { return boost::make_shared<StructComponent>(t); }

    boost::shared_ptr<StructComponent> m_shp;
};

class ClassB : public ClassA
{
public:
    ClassB()
    : ClassA(no_init())
    { m_shp = create(*this); }
};

class ClassC : public ClassA
{
public:
    ClassC()
    : ClassA(no_init())
    { m_shp = create(*this); }
};

そして、ここにさらに別の選択肢があります。今回は循環依存関係がなく、別の初期化コードをそれが属する場所に移動します。

struct StructComponent
{
  StructComponent() { /* minimum init */ }
};

class ClassA
{
public:
    ClassA() : m_shp( createA() ) { }

protected:
    struct no_init { };

    explicit
    ClassA(no_init)
    : m_shp()
    { }

    boost::shared_ptr<StructComponent> createA()
    {
      // something
    }

    boost::shared_ptr<StructComponent> m_shp;
};

class ClassB : public ClassA
{
public:
    ClassB()
    : ClassA(no_init())
    { m_shp = createB(); }

private:
    boost::shared_ptr<StructComponent> createB()
    {
      // something else
    }
};

class ClassC : public ClassA
{
public:
    ClassC()
    : ClassA(no_init())
    { m_shp = createC(); }

private:
    boost::shared_ptr<StructComponent> createC()
    {
      // something completely different
    }
};
于 2012-07-31T20:16:38.147 に答える
2

クラス のコンストラクターの実行中A、オブジェクトの型は ですA。したがって、基本実装は常に呼び出されます。したがって、実装を再入力する手間を省くことができます。

このコードは正しく設計されていません。少なくとも「正しい」という言葉の通常の意味では、正しく設計されていないコードは決して正しくありません。

C++ の規則が適切に機能しない場合、初期化にコンストラクターを使用する理由はありません。単純にメソッドにして、 Thing を呼び出すだけで、期待どおりの効果で、必要Initializeな仮想メソッドを呼び出すことができます。Initialize

于 2012-07-31T20:07:34.600 に答える