Множественное наследование C — почему у вас нет работы?

#c #inheritance #multiple-inheritance #diamond-problem

#c #наследование #множественное наследование #алмазная проблема

Вопрос:

Я пытаюсь разобраться в интересной проблеме множественного наследования.

Прародитель — это интерфейсный класс с несколькими методами:

 class A
{
public:
    virtual int foo() = 0;
    virtual int bar() = 0;
};
  

Тогда есть абстрактные классы, которые частично дополняют этот интерфейс.

 class B : public A
{
public:
    int foo() { return 0;}
};

class C : public A
{
public:
    int bar() { return 1;}
};
  

Класс, который я хочу использовать, наследуется от обоих родителей и указывает, какой метод должен откуда поступать с помощью директив using:

 class D : public B, public C
{
public:
    using B::foo;
    using C::bar;
};
  

Когда я пытаюсь создать экземпляр D, я получаю ошибки при попытке создать экземпляр абстрактного класса.

 int main()
{
    D d; //<-- Error cannot instantiate abstract class.

    int test = d.foo();
    int test2 = d.bar();

    return 0;
}
  

Может кто-нибудь помочь мне разобраться в проблеме и как наилучшим образом использовать частичные реализации?

Комментарии:

1. В ромбовидном шаблоне вам нужно использовать виртуальное наследование. Но я не верю, что это решит вашу проблему само по себе.

Ответ №1:

У вас нет алмазного наследования. B И C базовые классы D каждого имеют свой собственный A подобъект базового класса, потому что они практически не наследуются от A .

Итак, в D действительно есть четыре чисто виртуальные функции-члена, которые необходимо реализовать: A::foo и A::bar из B и A::foo и A::bar из C .

Вы, вероятно, хотите использовать виртуальное наследование. Объявления классов и списки базовых классов будут выглядеть следующим образом:

 class A
class B : public virtual A
class C : public virtual A
class D : public B, public C
  

Если вы не хотите использовать виртуальное наследование, то вам нужно переопределить две другие чисто виртуальные функции в D :

 class D : public B, public C
{
public:
    using B::foo;
    using C::bar;

    int B::bar() { return 0; }
    int C::foo() { return 0; }
};
  

Ответ №2:

Вам нужно создать свои базовые классы virtual , чтобы они могли правильно наследоваться. Общее правило заключается в том, что все непубличные функции-члены и базовые классы должны быть virtual , ЕСЛИ только вы не знаете, что делаете, и не хотите отключить обычное наследование для этого элемента / базы.

Комментарии:

1. «Общее правило заключается в том, что все общедоступные функции-члены и базовые классы должны быть виртуальными» Что? Чье это общее правило? Во всяком случае, общее правило, касающееся виртуальных функций-членов, заключается в том, чтобы сделать их закрытыми и иметь общедоступную невиртуальную функцию-член в базовом классе, вызывающую закрытую виртуальную функцию-член (это обсуждается в статье GotW «Виртуальность» ). Что касается виртуальных базовых классов… Я могу вспомнить только один случай за последние несколько лет, когда мне понадобилось использовать виртуальное наследование.

2. @JamesMcNellis » Я могу вспомнить только один случай за последние несколько лет, когда мне понадобилось использовать виртуальное наследование. » Сколько раз вам нужно использовать номера -виртуальное наследование?

3. @JamesMcNellis: Эта статья в основном полная чушь. Единственное, что получается правильно, это то, что большинство функций-членов (виртуальных или нет) должны быть частными. Наличие общедоступных невиртуальных функций-членов — это ожидаемая катастрофа, которая в основном означает, что класс не может быть подклассом.

4. @ChrisDodd О чем ты говоришь … Чувак, если ты говоришь то, что ты лично думаешь, по крайней мере, не притворяйся, что то, что ты говоришь, является фактом… gtkmm — это всего лишь один из многих примеров классов C , где есть всего несколько виртуальных функций, обычно защищенных, и классы по-прежнему полностью предназначены для подклассов (и они очень легко подразделяются на подклассы). Если вы предпочитаете, чтобы все ваши общедоступные методы были виртуальными — это нормально. Мне это не нравится, но я не буду рассказывать вам, как писать код. Просто не передавайте личные привычки другим людям, как если бы они были фактами…