С++: Derived + Base класс реализует единый интерфейс?
В С++ возможно ли иметь базовый плюс производный класс реализовать один интерфейс?
Например:
class Interface
{
public:
virtual void BaseFunction() = 0;
virtual void DerivedFunction() = 0;
};
class Base
{
public:
virtual void BaseFunction(){}
};
class Derived : public Base, public Interface
{
public:
void DerivedFunction(){}
};
void main()
{
Derived derived;
}
Это не удается, потому что Derived не может быть создан. Что касается компилятора, то интерфейс:: BaseFunction никогда не определяется.
Пока единственным решением, которое я нашел, было бы объявить функцию прохода в Derived
class Derived : public Base, public Interface
{
public:
void DerivedFunction(){}
void BaseFunction(){ Base::BaseFunction(); }
};
Есть ли лучшее решение?
EDIT: Если это имеет значение, вот проблема реального мира, с которой я использовал диалоги MFC.
У меня есть класс диалога (говорит MyDialog), который происходит из CDialog. Из-за проблем с зависимостями мне нужно создать абстрактный интерфейс (MyDialogInterface). Класс, который использует MyDialogInterface, должен использовать методы, специфичные для MyDialog, но также должен вызывать CDialog:: SetParent. Я просто решил это, создав MyDialog:: SetParent и передав его в CDialog:: SetParent, но задавался вопросом, есть ли лучший способ.
Ответы
Ответ 1
С++ не замечает, что функция, унаследованная от Base, уже реализует BaseFunction
: функция должна быть явно реализована в классе, полученном из Interface
. Измените его следующим образом:
class Interface
{
public:
virtual void BaseFunction() = 0;
virtual void DerivedFunction() = 0;
};
class Base : public Interface
{
public:
virtual void BaseFunction(){}
};
class Derived : public Base
{
public:
virtual void DerivedFunction(){}
};
int main()
{
Derived derived;
}
Если вы хотите уйти от реализации только одного из них, разделите Interface
на два интерфейса:
class DerivedInterface
{
public:
virtual void DerivedFunction() = 0;
};
class BaseInterface
{
public:
virtual void BaseFunction() = 0;
};
class Base : public BaseInterface
{
public:
virtual void BaseFunction(){}
};
class Derived : public DerivedInterface
{
public:
virtual void DerivedFunction(){}
};
class Both : public DerivedInterface, public Base {
public:
virtual void DerivedFunction(){}
};
int main()
{
Derived derived;
Base base;
Both both;
}
Примечание: main должен возвращать int
Примечание. Хорошей практикой является сохранение virtual
перед функциями-членами в производных, которые были виртуальными в базе, даже если это не требуется строго.
Ответ 2
Похоже, что это не совсем так, что Derived "is-a" Base, которая предполагает, что сдерживание может быть лучшей реализацией, чем наследование.
Кроме того, ваши производные функции-члены должны быть декалированы как виртуальные.
class Contained
{
public:
void containedFunction() {}
};
class Derived
{
public:
virtual void derivedFunction() {}
virtual void containedFunction() {return contained.containedFunction();}
private:
Containted contained;
};
Вы можете сделать содержащийся элемент ссылкой или умным указателем, если вы хотите скрыть детали реализации.
Ответ 3
Проблема заключается в том, что в вашем примере у вас есть две реализации Interface
, одна из которых идет от Base
, а другая - от Derived
. Это по дизайну на языке С++. Как уже указывалось, просто удалите базовый класс Interface
в определении Derived
.
Ответ 4
Я согласен с ответом на litb. Однако здесь есть возможность понять, как работают виртуальные функции и множественное наследование.
Если класс имеет несколько базовых классов, он имеет отдельные vtables для каждого базового класса. Derived
будет иметь структуру vtable, которая выглядит так:
Derived
vtable: Interface
BaseFunction*
DerivedFunction*
vtable: Base
BaseFunction*
Кроме того, каждый базовый класс сможет видеть только свою виртуальную таблицу. Когда Base
создается, он заполняет указатель Base::BaseFunction
в таблице vtable, но не может видеть vtable для интерфейса.
Если код, который вы предоставили, может скомпилировать, результирующая структура vtable экземпляра Derived
будет выглядеть так:
Derived
vtable: Interface
BaseFunction* = 0
DerivedFunction* = Derived::DerivedFunction
vtable: Base
BaseFunction* = Base::BaseFunction
Ответ 5
Я нашел одну вещь, лишенную ответа на яркий свет. Если у меня есть экземпляр Derived
, я могу получить DerivedInterface
и BaseInterface
. Но если у меня есть только DerivedInterface
, я не могу получить BaseInterface
, поскольку вывод DerivedInterface
из BaseInterface
не будет работать.
Но все это время я ограничивал себя компиляцией проверки времени по какой-то причине. Это DerivedInterface
отлично работает:
class DerivedInterface
{
public:
virtual void DerivedFunction() = 0;
BaseInterface* GetBaseInterface()
{return dynamic_cast<BaseInterface*>(this);}
};
void main()
{
Derived derived;
DerivedInterface* derivedInterface = &derived;
derivedInterface->GetBaseInterface()->BaseFunction();
}
Не пропустите функции, необходимые в Derived, и все счастливы. Конечно, это не строго интерфейс, но это прекрасно. Почему я не подумал об этом раньше?:)