0
#include<iostream>
using namespace std;

class A

{
   int a;
   int b;
   public:
   void eat()
   {
      cout<<"A::eat()"<<endl;
   }
};

class B: public A
{
   public:
   void eat()
   {

      cout<<"B::eat()"<<endl;

   }

};

class C: public A
{

   public:
   void eat()

   {

      cout<<"C::eat()"<<endl;

   }

};

class D: public B, C
{

};

int foo(A *ptr)
{

ptr->eat();

}
main()
{

D obj;
foo(&(obj.B)); //error. How do i call with D's B part.

}

上記の foo 呼び出しはコンパイル時エラーです。仮想継承を使わずにobjのB部分でfooを呼び出したい。それ、どうやったら出来るの。

また、仮想継承の場合、オフセット情報を vtable に格納する必要がある理由。これは、コンパイル時に決定できます。上記の場合、D のオブジェクトで foo を渡すと、コンパイル時にのみ D の A 部分のオフセットを計算できます。

4

4 に答える 4

6

2回継承

二重継承では、あいまいさが生じます。コンパイラは、2 つの A ベースのどちらを使用するかを認識できません。2 つの A ベースが必要な場合 (場合によってはこれが必要な場合もあります)、B または C にキャストすることでそれらを選択できます。ここでのデフォルト キャストから最も適切なのはstatic_cast(利用可能な最も弱いものとして) ですが、そうではありません。派生型にキャストしていないため、本当に必要です(ケースのニーズよりもまだ強力です)。カスタムsafe_castテンプレートは次のように機能します。

/// cast using implicit conversions only
template <class To,class From>
inline To safe_cast( const From &from ) {return from;}

main()
{

  D obj;
  foo(safe_cast<B *>(&obj)); //error. How do i call with D's B part.

}

コンパイル時の型 - テンプレートを使用

また、仮想継承の場合、オフセット情報を vtable に格納する必要がある理由。これは、コンパイル時に決定できます。上記の場合、D のオブジェクトで foo を渡すと、コンパイル時にのみ D の A 部分のオフセットを計算できます。

これは誤解です。現在記述されている foo 関数には、B * または C* を渡したとしても、A * 以外の ptr 型に関するコンパイル型情​​報はありません。コンパイル時に渡された型に基づいて foo が動作できるようにする場合は、テンプレートを使用する必要があります。

template <class TypeDerivedFromA>
int foo(TypeDerivedFromA *ptr)
{
  ptr->eat();
}

仮想継承

あなたの質問は仮想継承に言及しています。仮想継承を使用する場合は、次のように指定する必要があります。

class B: public virtual A ...

class C: public virtual A ...

これでコードはコンパイルされますが、このソリューションでは B::A または C::A を選択する方法がありません (A は 1 つしかありません)。

仮想機能

さらに、あなたの質問は、2 つの異なる概念、仮想継承 (2 つの中間基本クラス間で 1 つの基本クラスを共有することを意味します) と仮想関数 (基本クラス ポインターを介して派生クラス関数を呼び出すことができることを意味します) を混同しているようです)。B::eat を A ポインターを使用して呼び出す場合は、仮想関数を使用して、仮想継承を使用せずにこれを行うことができます (実際には、上記で説明したように、仮想継承によりそうすることができなくなります)。

class A
{
   int a;
   int b;

   public:
   virtual void eat()
   {
      cout<<"A::eat()"<<endl;
   }
};

仮想関数が受け入れられない場合、上記で説明したように、これに対するコンパイル時のメカニズムはテンプレートです。

于 2008-12-19T09:57:34.207 に答える
3

キャストを使用 -static_castここでは階層をキャストする必要があります。

main()
{
  D obj;
  foo(static_cast<B*>(&obj));
}
于 2008-12-19T09:52:41.753 に答える
1

まず、 にobjは B という名前のメンバーがありません。B から継承します。つまり、B のすべてのメンバーを自分のメンバーとして継承します。

あなたは呼び出すことができます:

foo(static_cast<B*>(&obj));
それを機能させるために。

于 2008-12-19T09:54:08.893 に答える
1

static_cast が機能するとは思わない。

foo 関数を使用している場合、コンパイラが知っているのは、パラメーターとして渡した型に関係なく、A へのポインターがあることだけです。

仮想継承を使用しない場合、A へのポインターから B 関数を呼び出す方法はないと思います。

于 2008-12-19T10:05:29.527 に答える