2

私はレガシーフレームワークに取り組んでいます。「A」が基本クラスで「B」が派生クラスだとしましょう。どちらのクラスも、いくつかの重要なフレームワークの初期化を行います。FWIW、それはACEライブラリを多用しています。

私には次のような状況があります。「B」のインスタンスが作成されます。しかし、「A」の ctor は、「B」からのみ実行できるいくつかの初期化に依存しています。

'B' がインスタンス化されると、'A' の ctor が 'B' の ctor の前に呼び出されます。このvirtualメカニズムは ctors からは機能しません。使用static functionsは除外されています ( static-initialization-order- fiasco のため)。

次のようにCRTPパターンを使用することを検討しました:-

template<class Derived>
class A {
public:
  A(){
    static_cast<Derived*>(this)->fun();
  }
};

class B : public A<B> {
public:
  B() : a(0) {
    a = 10;
  }
  void fun() { std::cout << "Init Function, Variable a = " << a << std::endl; }
private:
  int a;
};

しかし、イニシャライザ リストで初期化されたクラス メンバは、まだ実行されていないため、未定義の値を持ちます (上記の場合は fe 'a')。私の場合、そのようなフレームワークベースの初期化変数が多数あります。

この状況を処理するためのよく知られたパターンはありますか?

前もって感謝します、


更新

dribeas から提供されたアイデアに基づいて、この問題に対する一時的な解決策を思いつきました (本格的なリファクタリングは、今のところ私のタイムラインには合いません)。次のコードは同じことを示します:-

// move all A's dependent data in 'B' to a new class 'C'.
class C {
public:
   C() : a(10)
   {  }
   int getA() { return a; }
private:
   int a;

};

// enhance class A's ctor with a pointer to the newly split class
class A {
public:
   A(C* cptr)
   {
     std::cout << "O.K. B's Init Data From C:- " << cptr->getA() <<
std::endl;
   }

};

// now modify the actual derived class 'B' as follows
class B : public C, public A {
public:
   B()
     : A(static_cast<C*>(this))
   { }

}; 

同じことの詳細については、clc++.m のこのリンクを参照してください。Konstantin Oznobikhin によって提供された優れた一般的な解決策があります。

4

5 に答える 5

3

おそらくあなたができる最善のことはリファクタリングです。基本クラスをその派生型の1つに依存させることは意味がありません。

これは以前に行われたことがあり、開発者にかなりの苦痛を与えています。ACE_Taskクラスを拡張して、具体的な機能で拡張できる定期的なスレッドを提供し、定期的なスレッドコンストラクターからスレッドをアクティブ化して、テスト中などにそれを見つけます。多くの場合、それは機能しましたが、状況によっては、最も派生したオブジェクトが初期化される前にスレッドが実際に開始されました。

継承は強力な関係であり、必要な場合にのみ使用する必要があります。ブーストスレッドライブラリ(ドキュメントのみ、詳細を入力する必要はありません)、またはPOCOライブラリを見ると、問題が2つに分割されていることがわかります。スレッドクラスはスレッドの実行を制御し、渡されるメソッドを呼び出します。構築中のユーザーにとって:スレッド制御は実行される実際のコードから分離され、実行されるコードがコンストラクターへの引数として受け取られるという事実は、スレッドコンストラクターが呼び出される前に構築されたことを保証します。

たぶん、あなたはあなた自身のコードで同じアプローチを使うことができます。機能を2つに分割します。派生クラスが現在実行していることはすべて、階層の外に移動する必要があります(ブーストはファンクターを使用し、POCOはインターフェイスを使用し、最も適していると思われるものを使用します)。あなたがやろうとしていることのより良い説明がなければ、私は本当にこれ以上詳細に立ち入ることはできません。

あなたが試すことができるもう一つのこと(これは壊れやすいので、私は反対することをお勧めします)は、BクラスをAから独立したCクラスと、最初にCから、次にAから継承するBクラスに分割することです(そこに巨大な警告コメントがあります) )。これにより、CがAの前に構築されることが保証されます。次に、CサブオブジェクトをAの引数にします(インターフェイスを介して、またはテンプレート引数として)。これはおそらく最速のハックですが、良いハックではありません。コードを変更する意思がある場合は、正しく実行してください。

于 2010-01-12T08:27:21.327 に答える
2

おそらくレイジー初期化はあなたのためにそれを行います。初期化されているかどうかに関係なく、フラグをAに格納します。メソッドを呼び出すときはいつでも、フラグを確認してください。falseの場合は、Aを初期化し(Bのコンストラクターが実行されている)、フラグをtrueに設定します。

于 2010-01-12T08:27:47.677 に答える
2

まず、基本クラスのコンストラクターが派生クラスのコンストラクターで行われたことに依存している場合、あなたの設計は悪いと思います。本当はそうであってはなりません。基底クラスのコンストラクタが実行された時点では、基本的に派生クラスのオブジェクトは存在しません。

解決策として、ヘルパー オブジェクトを派生クラスから基本クラスのコンストラクターに渡すことが考えられます。

于 2010-01-12T08:21:12.743 に答える
1

それは悪いデザインであり、すでに述べたようにUBです。このような依存関係を「初期化」などの他のメソッドに移動することを検討し、派生クラス コンストラクターからこの初期化メソッドを呼び出してください (または、基本クラス データを実際に初期化する必要がある前の任意の場所)。

于 2010-01-12T09:18:29.243 に答える
0

うーん。したがって、私がこれを正しく読んでいる場合、「A」はレガシーコードの一部であり、問​​題に対する正しい答えは派生クラス B を使用することであると確信しています。

最も簡単な解決策は、関数型 (非 OOP) スタイルの静的ファクトリ関数を作成することだと思われます。

static B& B::makeNew(...);

あなたが静的な初期化順序の大失敗に遭遇すると言うことを除いて?初期化が行われていないため、この種のセットアップを使用するとは思いません。

さて、問題をさらに見てみると、「C」は「A」が行う必要のあるセットアップを「B」が行う必要があり、「A」のみが最初のディブを取得します。これは、継承が必要なためです。それで...構築順序を制御できる方法での偽の継承...?

class A
{
    B* pB;
public:
    rtype fakeVirtual(params) { return pB->fakeVirtual(params); }

    ~A()
    {
        pB->deleteFromA();
        delete pB;
        //Deletion stuff
    }

protected:
    void deleteFromB()
    {
        //Deletion stuff
        pB = NULL;
    }
}

class B
{
    A* pA;
public:
    rtype fakeInheritance(params) {return pA->fakeInheritance(params);}

    ~B()
    {
        //deletion stuff
        pA->deleteFromB();
    }

protected:
    friend class A;
    void deleteFromA()
    {
        //deletion stuff
        pA = NULL;
    }
}

冗長ですが、これは安全に継承を偽造し、B がそれを完了するまで A の構築を待つことができると思います。これもカプセル化されているため、A をプルできる場合は、A と B 以外を変更する必要はありません。

または、数歩戻って自問することもできます。使用しようとしている継承によって提供される機能は何ですか?また、他の手段でそれをどのように達成できますか? たとえば、CRTP は の代替として使用できvirtual、ポリシーは機能継承の代替として使用できます。(それが正しい言い回しだと思います)。私は上記のこれらのアイデアを使用しています。テンプレートを b/c にドロップするだけです。

于 2010-01-12T15:26:52.783 に答える