C++, обеспечивающий реализацию чисто виртуального метода из другой иерархии наследования

У меня есть класс Parent и два класса Child:

class Parent
{
};

class Child1: public Parent
{
};

class Child2 : public Parent
{
};

Я хотел бы вторую иерархию классов с классом Cousin наверху иерархии и подклассами, называемыми Cousin1, Cousin2 и т. д.:

class Cousin
{
    virtual void doUsefulWork() = 0;
};

class Cousin1 : public Cousin
{
    virtual void doUsefulWork() override
    {
        // One behaviour
    }
};

class Cousin2 : public Cousin
{
    virtual void doUsefulWork() override
    {
        // A different behaviour
    }
};

Каждый подкласс Child ДОЛЖЕН наследовать подкласс Cousin, чтобы иметь реализацию doUsefulWork():

class Child1 : public Parent, Cousin1
{
    // Has implementation of doUsefulWork() from Cousin1
};

Как защититься от того, чтобы автор подкласса Child забыл наследовать подкласс Cousin? Мне нужно что-то, что вызывает ошибку времени компиляции.

Каждый подкласс Child должен иметь доступ к реализации doUsefulWork().

Я думал о том, чтобы Cousin наследовать от Parent и определить чистый виртуальный doUsefulWork() в Parent, но тогда у меня возникнут сложные проблемы с множественным наследованием в подклассах Child?


person user997112    schedule 28.11.2018    source источник
comment
Я бы сделал наоборот и наследовал Parent от Cousin. Это по крайней мере вызовет ошибку компиляции, если автор класса Child забудет повторно реализовать doUsefulWork() или наследовать от класса, который имеет реализацию.   -  person dave    schedule 28.11.2018
comment
Или наоборот, class Parent : public Cousin { ... поможет.   -  person Eljay    schedule 28.11.2018
comment
я надеюсь, что имена только для примера здесь. Имхо аналог дочерний-родительский - один из худших, так как struct Child : Parent на самом деле означает, что каждый Child является-a Parent, неудивительно, что это вызывает путаницу, когда ОО понимается так   -  person 463035818_is_not_a_number    schedule 28.11.2018
comment
Я не уверен, что class Parent: public Cousin { такая уж хорошая идея. Получение class Child1: public Parent, Cousin1 { будет иметь базовый класс Cousin дважды (и это пахнет необходимостью виртуального наследования...)   -  person Scheff's Cat    schedule 28.11.2018
comment
Чего я не понимаю: Child1 доступ с помощью указателя на Parent делает виртуальный метод doUsefulWork() невидимым. Это указатель на Cousin, который необходим. Если, в свою очередь, Child3 случайно не является производным от какого-либо CousinX с переопределенным doUsefulWork(), назначение его указателю на Cousin должно вызвать проблемы. Есть что-то тонкое, о чем я еще не знаю.   -  person Scheff's Cat    schedule 28.11.2018
comment
Обязательно ли вам унаследовать Cousin? В любом случае ассоциация была бы намного чище. Если вам нужно получить доступ к защищенным членам Cousin, как насчет того, чтобы сделать их friends?   -  person Detonar    schedule 28.11.2018
comment
@Scheff Очевидно, что потомок будет наследоваться только от Родителя напрямую.   -  person jlanik    schedule 28.11.2018
comment
@jlanik Я так понял, что Cousin1 должен обеспечить реализацию путем переопределения. Cousin как раз наводит на это нужду (по чистой виртуалке). (как я понял)   -  person Scheff's Cat    schedule 28.11.2018
comment
@Шефф Я вижу. Тогда дети могут наследовать Кузена1 или Кузена2. Это алмаз, но он должен быть sdafe, пока Cousin содержит только чистые виртуальные методы.   -  person jlanik    schedule 28.11.2018
comment
@jlanik На самом деле, я думаю, что ничего не нужно менять. Для вызова виртуального метода doUsefulWork() указатель объекта должен иметь тип Cousin* (не Parent*). Назначение указателя ChildX на Cousin* вызовет жалобы компилятора, если нет наследования от какого-либо производного CousinX. На самом деле, я скорее вижу опасность, что ChildX правильно получено из CousinX, но не из Parent.   -  person Scheff's Cat    schedule 28.11.2018
comment
@Scheff Но он ничего не говорит о том, будет ли доступ к дочерним экземплярам осуществляться через указатель или ссылку Cousin или каким-либо другим способом. Он просто говорит, что хочет убедиться, что если вы создаете новый класс, наследуемый от Parent, вам необходимо реализовать метод void doUsefulWork(). Что мне говорит Parent: public Cousin.   -  person jlanik    schedule 28.11.2018
comment
@jlanik Я заглянул в профиль ОП. Мне кажется, что OP имеет более сильный опыт в Java, где интерфейсы более распространены, чем множественное наследование. Что касается этого фона, вопрос ОП почему-то имеет для меня смысл. ОП не сказал, как она/он на самом деле хочет получить доступ к ChildX. Эта информация, вероятно, будет полезна, чтобы прояснить это.   -  person Scheff's Cat    schedule 28.11.2018
comment
@user463035818 user463035818 Да, я заменил имена классов на основные.   -  person user997112    schedule 28.11.2018
comment
Непонятно, каков ваш дизайн с точки зрения отношений между классами.   -  person curiousguy    schedule 07.12.2018


Ответы (1)


Как я могу защититься от автора подкласса Child, забывшего наследовать подкласс Cousin? Я хотел бы что-то, что генерирует ошибку времени компиляции.

Причина, по которой вы реализуете эти интерфейсы, заключается в том, что должны быть функции, которые используют эти интерфейсы. Если объект не реализует интерфейс, вы получаете ошибку компилятора при передаче объекта этой функции, что действует как принудительная проверка того, что объект должен реализовать этот интерфейс.

person Maxim Egorushkin    schedule 28.11.2018