Я пытаюсь использовать множественное наследование для решения сложной иерархии, которую я разрабатываю. Ситуация следующая:
class A {
virtual void foo();
}
class B {
virtual void foo();
}
class C : public B {
}
class D : public A, public C {
void foo() { ... }
}
class ClientClass {
void method() {
A *a = new D();
a->foo();
}
Что мне интересно, так это: D
иметь только одну функцию foo()
в конце? Я думаю об этом, потому что метод является виртуальным у обоих родителей, поэтому им нужно коллимировать на одного и того же, но я думаю, что это просто потому, что я пришел с Java и чувствую, что в C ++ он может быть другим. Я должен объявить виртуальную функцию foo()
дважды, потому что ClientClass
не знает B
или же C
но просто A
, Это требование, которое я хотел бы соблюдать.
РЕДАКТИРОВАТЬ: применяется ли тот же ответ, даже если оба foo()
в A
а также B
чисто виртуальные? (например = 0
)
На самом деле, реализация D::foo
переопределит оба A::foo
а также B::foo
в то же время.
Разумеется, обе унаследованные функции будут по-прежнему доступны для вызова по их полному имени. Но это не отличается от случая единственного наследования.
О ClientClass
Вы создаете D
объект, а затем вызвать foo
через указатель на A
, Итак D::foo
переопределение будет вызвано.
Если вы хотите разные версии foo
для переопределения A::foo
а также C::foo
(например, потому что они не связаны, но иногда называются одинаково), вам понадобится немного работы:
class A2 : public A
{
public:
virtual void foo()
{
A_foo();
}
virtual void A_foo()
{
A::foo();
}
};
class C2 : public C
{
public:
virtual void foo()
{
C_foo();
}
virtual void C_foo()
{
C::foo();
}
};
class D: public A2, C2
{
public:
virtual void A_foo()
{ /* ... */ }
virtual void C_foo()
{ /* ... */ }
};
Теперь использование:
{
A *a = new D;
a->foo(); //will call A2::foo -> D::A_foo
B *b = new D;
b->foo(); //will call C2::foo -> D::C_foo
D *d = new D;
d->foo(); //error: ambiguous call!
}
Буду иметь два. Они могут быть доступны с A :: foo()
а также B :: foo()
внутри класса.
Класс D
имеет три функции foo
: D::foo
, B::foo
а также A::foo
,
поскольку foo
виртуален в обоих A
а также B
неполный вызов функции отправляется динамически самой производной функции:
void f(A & a) { a.foo(); }
void g(B & b) { b.foo(); }
int main()
{
D d;
f(d); // calls d.D::foo();
g(d); // calls d.D::foo();
d.foo(); // calls d.D::foo();
d.B::foo(); // calls d.B::foo();
d.A::foo(); // calls d.A::foo();
}
Вы столкнулись с классической проблемой множественного наследования, возможно, самой важной, предполагающей его весьма ограниченное использование (посредством концепции миксинов или интерфейсов / виртуальных классов).
Посмотри на этот пост от S.O для более полного объяснения и различных альтернатив у вас есть.
В этом коде:
class ClientClass {
void method() {
A *a = new D();
a->foo();
}
Ду Фу побежит. И вы все равно можете получить доступ к A foo с помощью a-> A :: foo ().
Обратите внимание, что при инициализации «a» вы не можете достичь B foo ().
Но каждый раз, когда вы будете создавать экземпляр D, он будет использовать D foo, поскольку обе функции foo объявлены виртуальными.
Для каждого класса в вашем коде у вас есть таблица функций.
Таблица для каждого объекта определяется его тип. Это означает, что A * a = new D()
имеет таблицу функций.
Если функция имеет виртуальный флаг перед ней, это означает, что если экземпляр производного класса является сборкой, ее таблица будет содержать указатель на функцию производного класса.
Это означает, что в вашем коде в ClinetClass объект ‘a’ имеет таблицу функций A, где это функция foo, содержащая указатель на функцию D foo.
Просто заметьте, что в случае, если у D не было своего собственного foo, вы должны написать
D * d = new D();
d-> foo();
Вы получите ошибку компиляции, если он не знает, какую функцию foo он должен использовать.