Dziedziczenie wirtualne, które sugerowała Laura, jest oczywiście rozwiązaniem problemu. Ale nie kończy się to na posiadaniu tylko jednego InterfaceA. Ma również "skutki uboczne", np. patrz https://isocpp.org/wiki/faq/multiple-inheritance#mi-delegate-to-sister. Ale jeśli się do tego przyzwyczaisz, może się przydać.
Jeśli nie chcesz skutków ubocznych, można użyć szablonu:
struct InterfaceA
{
virtual void methodA() = 0;
};
template<class IA>
struct ClassA : public IA //IA is expected to extend InterfaceA
{
void methodA() { 5+1;}
};
struct InterfaceB : public InterfaceA
{
virtual void methodB() = 0;
};
struct ClassAB
: public ClassA<InterfaceB>
{
void methodB() {}
};
int main()
{
InterfaceB* test = new ClassAB();
test->methodA();
}
Więc mają dokładnie jedną klasę nadrzędną.
Ale wygląda bardziej brzydko, gdy istnieje więcej niż jedna klasa "udostępniona" (interfejs A jest "udostępniony", ponieważ znajduje się na wierzchu "przerażającego diamentu", patrz tutaj https://isocpp.org/wiki/faq/multiple-inheritance opublikowany przez Laurę). Zobacz przykład (co będzie, jeśli ClassA realizuje interfaceC zbyt):
struct InterfaceC
{
virtual void methodC() = 0;
};
struct InterfaceD : public InterfaceC
{
virtual void methodD() = 0;
};
template<class IA, class IC>
struct ClassA
: public IA //IA is expected to extend InterfaceA
, public IC //IC is expected to extend InterfaceC
{
void methodA() { 5+1;}
void methodC() { 1+2; }
};
struct InterfaceB : public InterfaceA
{
virtual void methodB() = 0;
};
struct ClassAB
: public ClassA<InterfaceB, InterfaceC> //we had to modify existing ClassAB!
{
void methodB() {}
};
struct ClassBD //new class, which needs ClassA to implement InterfaceD partially
: public ClassA<InterfaceB, InterfaceD>
{
void methodB() {}
void methodD() {}
};
złą rzeczą, że trzeba zmodyfikować istniejącą ClassAB. Ale można napisać:
template<class IA, class IC = interfaceC>
struct ClassA
Potem pozostaje niezmieniona ClassAB:
struct ClassAB
: public ClassA<InterfaceB>
I masz domyślną implementację dla parametru szablonu IC.
Którą drogą należy wybrać. Preferuję szablon, kiedy jest prosty do zrozumienia.Jest to dość trudne do uzyskania w zwyczaju, że B :: incrementAndPrint() i C :: incrementAndPrint() będzie drukować różne wartości (nie swoim przykładzie), patrz poniżej:
class A
{
public:
void incrementAndPrint() { cout<<"A have "<<n<<endl; ++n; }
A() : n(0) {}
private:
int n;
};
class B
: public virtual A
{};
class C
: public virtual A
{};
class D
: public B
: public C
{
public:
void printContents()
{
B::incrementAndPrint();
C::incrementAndPrint();
}
};
int main()
{
D d;
d.printContents();
}
a wyjście:
A have 0
A have 1
Dzięki za wyjaśnienie Laura. –
Myślałem, że gdy funkcja zostanie zadeklarowana, 'virtual' jest zawsze wirtualna w hierarchii klas, niezależnie czy klasy pochodne używają' wirtualnego' lub nie, podczas definiowania – johnbakers