C ++: Производный + Базовый класс реализует единый интерфейс? - PullRequest
12 голосов
/ 14 ноября 2008

В C ++ возможно ли, чтобы базовый плюс производный класс реализовывал один интерфейс?

Например:

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 не может быть создан. Что касается компилятора, Interface :: BaseFunction никогда не определяется.

Пока единственное решение, которое я нашел, это объявить сквозную функцию в Derived

class Derived : public Base, public Interface
{
    public: 
        void DerivedFunction(){}
        void BaseFunction(){ Base::BaseFunction(); }
};

Есть ли лучшее решение?


РЕДАКТИРОВАТЬ: Если это имеет значение, вот реальная проблема у меня возникла с использованием диалогов MFC.

У меня есть класс диалога (скажем, MyDialog), производный от CDialog. Из-за проблем с зависимостями мне нужно создать абстрактный интерфейс (MyDialogInterface). Класс, который использует MyDialogInterface, должен использовать методы, специфичные для MyDialog, но также должен вызывать CDialog :: SetParent. Я просто решил эту проблему, создав MyDialog :: SetParent и передав его в CDialog :: SetParent, но мне было интересно, есть ли лучший способ.

Ответы [ 5 ]

16 голосов
/ 14 ноября 2008

C ++ не замечает, что функция, унаследованная от 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 перед функциями-членами в производных, которые были виртуальными в базе, даже если это не является строго обязательным.

4 голосов
/ 14 ноября 2008

Похоже, что это не совсем тот случай, когда Derived "is-a" Base предполагает, что ограничение может быть лучшей реализацией, чем наследование.

Кроме того, ваши производные функции-члены также должны быть объявлены как виртуальные.

class Contained
{
    public:
        void containedFunction() {}
};

class Derived
{
    public:
        virtual void derivedFunction() {}
        virtual void containedFunction() {return contained.containedFunction();}
    private:
        Containted contained;
};

Вы можете сделать содержащийся элемент ссылкой или умным указателем, если хотите скрыть детали реализации.

1 голос
/ 14 ноября 2008

Согласен с ответом по литб. Однако здесь есть возможность понять, как работают виртуальные функции и множественное наследование.

Когда у класса есть несколько базовых классов, у него есть отдельные таблицы для каждого базового класса. Derived будет иметь структуру vtable, которая выглядит следующим образом:

Derived
 vtable: Interface
   BaseFunction*
   DerivedFunction*
 vtable: Base
   BaseFunction*

Кроме того, каждый базовый класс сможет видеть только свой собственный vtable. Когда создается экземпляр Base, он заполняет указатель Base::BaseFunction в виртуальной таблице, но не видит виртуальную таблицу для интерфейса.

Если предоставленный вами код может скомпилироваться, результирующая структура vtable экземпляра Derived будет выглядеть следующим образом:

Derived
 vtable: Interface
   BaseFunction* = 0
   DerivedFunction* = Derived::DerivedFunction
 vtable: Base
   BaseFunction* = Base::BaseFunction
1 голос
/ 14 ноября 2008

Проблема в том, что в вашем примере у вас есть две реализации Interface, одна из которых Base, а другая Derived Это сделано на языке C ++. Как уже указывалось, просто удалите базовый класс Interface из определения Derived.

0 голосов
/ 15 ноября 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...