Множественное наследование + путаница виртуальной функции

  • Если token является уникальным , вы можете сделать:

    getInvites().stream()
                .filter(i -> token.equals(i.getToken()))
                .filter(i -> !i.isConfirm())
                .findAny()
                .orElseThrow(IllegalArgumentException::new)
                .setConfirm(true);
    
  • .

17
задан Lightness Races with Monica 27 July 2011 в 15:04
поделиться

6 ответов

Если Вы не перезаписываете fn снова в D, нет это не возможно. Поскольку нет никакого заключительного сверхнаездника в объекте D: И C и B переопределение A::fn. У Вас есть несколько опций:

  • Отбрасывание или C::fn или B::fn. Затем тот, который все еще переопределяет A::fn, имеет заключительного сверхнаездника.
  • Место заключительный сверхнаездник в D. Затем тот переопределения A::fn, а также fn в [1 111] и B.

, Например, следующие результаты в ошибке времени компиляции:

#include <iostream>

class A {
public:
    virtual void fn() { }
};

class B : public virtual A {
public:
    virtual void fn() { }
};

class C : public virtual A {
public:
    virtual void fn() { }
};

// does not override fn!!
class D : public B, public C {
public:
    virtual void doit() {
        B::fn();
        C::fn();
    }
};

int main(int argc, char **argv) {
  D d;
  d.doit();
  return 0;
}

Вы можете, однако произойти невиртуальный из в C и B, но тогда у Вас больше нет ромбовидного наследования. Таким образом, каждый элемент данных в A появляется дважды в B и C, потому что Вы имеете два, базовый класс подвозражает в объекте D. Я рекомендовал бы Вам заново продумать тот дизайн. Попытайтесь устранить двойные объекты как этот, который требует виртуального наследования. Это часто вызывает такой вид конфликтующих ситуаций.

случай А, очень похожий на это, - когда Вы хотите переопределить определенную функцию. Предположите, что у Вас есть виртуальная функция с тем же именем в B и C (теперь без общей базы A). И в D Вы хотите переопределить каждую функцию, но дать различное поведение каждому. Завися, вызываете ли Вы функцию с указателем B или указателем C, у Вас есть другое поведение. Часть III Множественного наследования Herb Sutter описывает хороший способ сделать это. Это могло бы помочь Вам выбрать свой дизайн.

19
ответ дан 30 November 2019 в 12:58
поделиться

Первый вопрос, да, B и C могут определить fn() как виртуальную функцию. Во-вторых, D может, конечно, получить доступ B::fn() и C::fn() при помощи оператора объема:: Третий вопрос: D должен, по крайней мере, знать B и C, так как необходимо определить их в списке наследования. Можно использовать шаблоны для разрешения типам B и открытого C:

class A
{
public:
   virtual ~A() {}
   virtual void fn() = 0;
};

class B: public A
{
public:
   virtual ~B() {}
   virtual void fn(){ std::cout << "B::fn()" << std::endl; }
};

class C: public A
{
public:
   virtual ~C() {}
   virtual void fn(){ std::cout << "C::fn()" << std::endl; }
};

template <typename TypeB, typename TypeC>
class D: public TypeB, public TypeC
{
public:
   void Do()
   {
      static_cast<TypeB*>(this)->fn();
      static_cast<TypeC*>(this)->fn();
   }
};

typedef D<B, C> DInst;

DInst d;
d.Do();

О желании автоматически перечислить весь fn () функции всех классов, которым наследовался D: я не уверен, возможно ли это, не обращаясь к MPL. По крайней мере, можно расширить мой пример выше с помощью версий, которые имеют дело с 3 и больше шаблонных параметров, но я предполагаю, что существует верхнее (внутренний компилятор-) предел количества параметров шаблона класса.

4
ответ дан 30 November 2019 в 12:58
поделиться

Вы могли бы хотеть посмотреть Loki TypeLists, если действительно необходимо быть в состоянии отследить родословную и перечислить через типы. Я не уверен, действительно ли то, что Вы просите, возможно без набора работы. Удостоверьтесь, что Вы не сверхразработка здесь.

На немного отличающейся ноте, если Вы собираетесь использовать MI этим способом (т.е. страшный ромб ), тогда необходимо быть очень явными, о котором виртуальном участнике Вы хотите. Я не могу думать о хорошем случае, где Вы хотите выбрать семантику B::fn() более чем C::fn(), явно не принимая решение при записи D. Вы, вероятно, отберете один другой (или даже оба) на основе того, что делает отдельный метод. Как только Вы приняли решение, требование - то, что наследованные изменения не изменяют ожидания или семантический интерфейс.

, Если Вы действительно волнуетесь по поводу загрузки нового класса, скажите E вместо, говорят B, где E не убывает от B, но предлагает тот же интерфейс, тогда необходимо действительно использовать шаблонный подход, хотя я не уверен, почему существует static_cast<> там...

struct A {
    virtual ~A() {}
    virtual void f() = 0;
};
struct B: A {
    virtual void f() { std::cout << "B::f()" << std::endl; }
};
struct C: A {
    virtual void f() { std::cout << "C::f()" << std::endl; }
};

template <typename Base1, typename Base2>
struct D: Base1, Base2 {
    void g() { Base1::f(); Base2::f(); }
};

int main() {
    D<B,C> d1;
    D<C,B> d2;
    d1.g();
    d2.g();
    return 0;
}

// Outputs:
//   B::f()
//   C::f()
//   C::f()
//   B::f()

хорошо работает и кажется немного легче посмотреть на.

2
ответ дан 30 November 2019 в 12:58
поделиться

Уже существует несколько вопросов то соглашение с этим. Кажется, что у нас заканчиваются вопросы спросить. Возможно, поле поиска должно быть больше, чем кнопка Ask Question.

Видят

1
ответ дан 30 November 2019 в 12:58
поделиться

Вы не можете перечислить определения fn () в родословной. C++ испытывает недостаток в отражении. Единственным путем я могу вообразить, гигантский цикл, тестирующий идентификатор типа всех возможных предков. И повреждает воображать это.

1
ответ дан 30 November 2019 в 12:58
поделиться

Vividos уже ответил на основную часть сообщения. Даже если я использовал бы оператор объема вместо более громоздкого static_cast<> + разыменовывают оператор.

В зависимости от задачи под рукой, возможно, можно изменить отношения наследования от D до B и C для меньшего связывающегося состава (плюс возможно наследование от A). Это предполагает, что Вам не нужен D, который будет использоваться полиморфно или в качестве B или в качестве C, и что Вы действительно не требуете B и C совместное использование того же основного экземпляра.

, Если Вы выбираете состав, можно получить B и C как аргументы конструктору как ссылки/указатели типа A, делая D полностью не знающий о типах B и C. В той точке можно использовать контейнер для содержания как много производные объекты. Ваша собственная реализация fn () (если Вы так решаете), или любой другой метод.

0
ответ дан 30 November 2019 в 12:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: