27

インターフェイスとして機能する抽象基本クラスがあります。

抽象クラスの半分を実装する派生クラスの 2 つの「セット」があります。(一方の「セット」は初期化に関連する抽象仮想メソッドを定義し、もう一方の「セット」は実際の「作業」に関連するメソッドを定義します。)

次に、複数の継承を使用して完全に定義されたクラスを構築する派生クラスを作成します (それ自体は何も追加しません)。

だから:(悪い疑似コード)

class AbsBase {
  virtual void init() = 0;
  virtual void work() = 0;
}

class AbsInit : public AbsBase {
  void init() { do_this(); }
  // work() still abs
}

class AbsWork : public AbsBase {
  void work() { do_this(); }
  // init() still abs
}

class NotAbsTotal : public AbsInit, public AbsWork {
  // Nothing, both should be defined
}

まず、これでいいの?同じ Base から派生した 2 つのクラスを継承できますか? (そうだといい)。

ただし、これが「本当の問題」です(例を単純化するために少し上に嘘をつきました)。

私が実際に行ったことは、非抽象アクセサー メソッドを基本クラスに追加することです。

class AbsBase {
public:
  void init() { init_impl(); }
  void work() { work_impl(); }

private:
  virtual void init_impl() = 0;
  virtual void work_impl() = 0;
}

一般的なイディオムは、すべての仮想メソッドを非公開にすることです。

残念ながら、現在、AbsInit と AbsWork の両方がこれらのメソッドを継承しているため、NotAbsTotal は「それぞれ 2 つ」を継承しています (コンパイル時に実際に起こっていることを解体している可能性があることに気付きました)。

とにかく、g ++は、クラスを使用しようとすると「メンバーinit()のリクエストがあいまいです」と不平を言います。

AbsBase クラスを純粋なインターフェイスとして使用していれば、これは回避できたと思います (上の例が有効であると仮定します)。

だから: - 私は私の実装から離れていますか?- これは、仮想メソッドを非公開にするイディオムの制限ですか? - コードをリファクタリングして、やりたいことを実行するにはどうすればよいですか? (1 つの共通インターフェースを提供しますが、メンバー関数の「セット」の実装を交換する方法を許可します)

編集:

私は最初のものではないようです: http://en.wikipedia.org/wiki/Diamond_problem

ここでは仮想継承が解決策のようです。以前に仮想継承について聞いたことがありますが、それについて頭を悩ませたことはありません。私はまだ提案を受け付けています。

4

5 に答える 5

35

仮想継承をしたいようです。それが実際に良いアイデアであることが判明するかどうかは別の問題ですが、次のようにします。


class AbsBase {...};
class AbsInit: public virtual AbsBase {...};
class AbsWork: public virtual AbsBase {...};
class NotAbsTotal: public AbsInit, public AbsWork {...};

基本的に、デフォルトの非仮想多重継承には、派生クラスの各基本クラスのコピーが含まれ、すべてのメソッドが含まれます。これが、AbsBase のコピーが 2 つある理由です。また、メソッドの使用があいまいな理由は、両方のメソッドのセットが読み込まれているためです。C++ は、アクセスするコピーを知る方法がありません!

仮想継承は、仮想基底クラスへのすべての参照を 1 つのデータ構造に凝縮します。これにより、基本クラスのメソッドが再び明確になります。ただし、注意: 2 つの中間クラスに追加のデータがある場合、コードが共有仮想基本クラスを見つけられるようにするために、実行時のオーバーヘッドがわずかに増える可能性があります。

于 2008-10-31T19:47:54.540 に答える
1

それはほとんどの震えを与えますが、行うことができます。

「仮想継承」を使用する必要があります。その構文は次のようなものです

class AbsInit: public virtual AbsBase {...};
class AbsWork: public virtual AbsBase {...};
class NotAbsTotal: public AbsInit, public AbsWork {...};

次に、使用する関数を指定する必要があります。

NotAbsTotal::work()
{
    AbsInit::work_impl();
}

(正しい構文で更新)

于 2008-10-31T19:43:29.337 に答える
1

継承を仮想として宣言する必要があります。

struct AbsBase {
          virtual void init() = 0;
          virtual void work() = 0;
};

struct AbsInit : virtual public AbsBase {
          void init() {  }
};

struct AbsWork : virtual public AbsBase {
          void work() { }
};

struct NotAbsTotal : virtual public AbsInit, virtual public AbsWork {
};

void f(NotAbsTotal *p)
{
        p->init();
}

NotAbsTotal x;
于 2008-10-31T19:46:01.220 に答える
0

ここで何をモデル化しようとしているのかという観点から考え始める必要があります。

パブリック継承は、「isa」関係をモデル化するためにのみ使用する必要があります。たとえば、犬は動物、四角形は形などです。

Scott Meyer の著書『Effective C++』を参照して、OO 設計のさまざまな側面が何としてのみ解釈されるべきかについての優れたエッセイを読んでください。

編集:これまでに提供された回答は技術的に正しいが、モデル化しようとしている問題に対処しているとは思わないことを忘れていました。それが問題の核心です!

HTH

乾杯、

ロブ

于 2008-11-01T01:05:26.113 に答える