Подтвердить что ты не робот

С++: 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, но задавался вопросом, есть ли лучший способ.

4b9b3361

Ответ 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, и все счастливы. Конечно, это не строго интерфейс, но это прекрасно. Почему я не подумал об этом раньше?:)