web-dev-qa-db-ja.com

2つの派生クラスからの多重継承

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

抽象クラスの半分を実装する、派生クラスの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
}

まず、これを行うことはできますか?同じベースから派生した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

仮想継承がここでのソリューションのようです。以前に仮想継承について聞いたことがありますが、私はそれに頭を悩ませていません。私はまだ提案を受け入れています。

26
mmocny

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


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

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

仮想継承は、仮想基本クラスへのすべての参照を1つのデータ構造に圧縮します。これにより、基本クラスのメソッドが再び明確になります。ただし、注意:2つの中間クラスに追加のデータがある場合は、コードが共有仮想基本クラスを見つけられるようにするために、いくつかの小さな追加のランタイムオーバーヘッドが発生する可能性があります。

35
comingstorm

それはできるが、ほとんどの震えを与える。

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

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

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

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

(正しい構文で更新)

1
James Curran

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

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;
1

ここでモデル化しようとしているものに関して考え始める必要があります。

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

OO設計のさまざまな側面を解釈する必要があるだけのことについての優れたエッセイについては、Scott Meyerの本「Effective C++」をご覧ください。

編集:私はこれまでに提供された答えは技術的に正しいが、どれもあなたがモデル化しようとしている問題に対処していないと私が言うのを忘れており、それがあなたの問題の核心です!

HTH

乾杯、

ロブ

0
Rob Wells

下記のリンクで良いシンプルな例を見つけました。この記事では、長方形の面積と周長を計算するためのサンプルプログラムを使用して説明します。確認できます。

マルチレベル継承は、1つの派生クラスが複数の基本クラスから継承する継承階層です。続きを読む..

http://www.mobihackman.in/2013/09/multiple-inheritance-example.html

0
Techman92