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

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

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

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

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

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

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


person Michael    schedule 03.05.2011    source источник
comment
В ромбовидном шаблоне вам нужно использовать виртуальное наследование. Но я не верю, что это решит вашу проблему само по себе.   -  person David    schedule 03.05.2011


Ответы (2)


У вас нет алмазного наследства. Базовые классы 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; }
};
person James McNellis    schedule 03.05.2011

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

person Chris Dodd    schedule 03.05.2011
comment
Общее правило заключается в том, что все общедоступные функции-члены и базовые классы должны быть виртуальными. Что? Чье это общее правило? Во всяком случае, общее правило, касающееся виртуальных функций-членов, состоит в том, чтобы сделать их закрытыми и иметь общедоступную невиртуальную функцию-член в базовом классе, вызывающую частную виртуальную функцию-член (это обсуждается в статье GotW Виртуальность). Что касается виртуальных базовых классов... Я могу вспомнить только один случай за последние несколько лет, когда мне нужно было использовать виртуальное наследование. - person James McNellis; 03.05.2011
comment
@JamesMcNellis Я могу вспомнить только один случай за последние несколько лет, когда мне нужно было использовать виртуальное наследование. Сколько раз вам нужно было использовать не -виртуальное наследование? - person curiousguy; 19.11.2011
comment
@JamesMcNellis: Эта статья в основном полная чушь. Единственное, что получается правильно, это то, что большинство функций-членов (виртуальных или нет) должны быть закрытыми. Наличие общедоступных невиртуальных функций-членов — это катастрофа, ожидающая своего часа, и в основном означает, что класс не может быть подклассом. - person Chris Dodd; 03.08.2012
comment
@ChrisDodd О чем ты говоришь... чувак, если ты говоришь то, что думаешь лично, то хотя бы не притворяйся, что говоришь, как факт... gtkmm — это всего лишь один из многих примеров классов C++, где есть всего несколько виртуальных функций, обычно защищенных, и классы по-прежнему полностью предназначены для создания подклассов (и они являются очень легко подклассами). Если вы предпочитаете, чтобы все ваши общедоступные методы были виртуальными, это нормально. Мне это не нравится, но я не буду рассказывать вам, как писать код. Только не передавайте личные привычки другим людям, как если бы они были фактами... - person cfa45ca55111016ee9269f0a52e771; 16.02.2013