Множественное наследование - PullRequest
0 голосов
/ 19 декабря 2008
#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 является ошибкой времени компиляции. Я хочу вызвать foo с частью B объекта obj, не используя виртуальное наследование. Как я могу это сделать.

Кроме того, в случае виртуального наследования, почему информация о смещении должна храниться в vtable. Это можно определить во время самой компиляции. В приведенном выше случае, если мы передадим foo с объектом D, только во время компиляции мы сможем вычислить смещение части A D.

Ответы [ 4 ]

6 голосов
/ 19 декабря 2008

Наследование дважды

При двойном наследовании у вас есть неоднозначность - компилятор не может знать, какую из двух баз A вы хотите использовать. Если вы хотите иметь две базы 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.

}

Типы времени компиляции - используйте шаблоны

Кроме того, в случае виртуального наследования, почему информация о смещении должна быть хранится в виртуальной таблице. Это может быть определяется во время компиляции. В приведенном выше случае, если мы передаем Foo с Объект D, во время компиляции только мы можно рассчитать смещение D's A часть.

Это заблуждение. Функция foo в том виде, в каком она написана, теперь не имеет информации о типе компиляции о типе ptr, кроме A *, даже если вы передаете B * или C *. Если вы хотите, чтобы 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), поэтому, вероятно, это не то, о чем вы.

Виртуальные функции

Более того, ваши вопросы, кажется, путают две разные концепции: виртуальное наследование (что означает разделение одного базового класса между двумя промежуточными базовыми классами) и виртуальные функции (что означает, что функция производного класса может вызываться через указатель базового класса). Если вы хотите, чтобы B :: eat вызывался с использованием указателя A, вы можете сделать это без виртуального наследования (фактически виртуальное наследование помешает вам сделать это, как описано выше), используя виртуальные функции:

class A
{
   int a;
   int b;

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

Если виртуальные функции для вас неприемлемы, механизмом компиляции для этого являются шаблоны, как объяснено выше.

3 голосов
/ 19 декабря 2008

Используйте приведение - static_cast требуется здесь, чтобы создать иерархию.

main()
{
  D obj;
  foo(static_cast<B*>(&obj));
}
1 голос
/ 19 декабря 2008

Я не думаю, что static_cast будет работать.

Когда вы используете функцию foo, все, что знает компилятор, это то, что у вас есть указатель на A независимо от типа, который вы передали в качестве параметра.

Если вы не используете виртуальное наследование, то я думаю, что нет способа вызвать функцию B из указателя на A.

1 голос
/ 19 декабря 2008

Прежде всего, obj не имеет члена с именем B. Он наследуется от B, что означает, что он наследует всех членов B как своих собственных.

Вы можете позвонить:

foo(static_cast<B*>(&obj));
чтобы все заработало.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...